Klant: Useq

Project: Spatial Organ Atlas

Dataset: Kidney

date: 09 25 Thu 16 March, 2023

loading dependencies Please make sure the following packages are installed and required libraries can be loaded:

  • install.packages(“pkgbuild”) // pkgbuild::check_build_tools()
  • install.packages(“devtools”)
  • devtools::install_github(“Nanostring-Biostats/NanoStringNCTools”)
  • devtools::install_github(“Nanostring-Biostats/GeomxTools”, ref = “dev”)
  • BiocManager::install(“GeoMxWorkflows”)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“SpatialDecon”)
  • BiocManager::install(“GSVA”)
  • install.packages(“plotly”)
  • install.packages(“DT”)
  • install.packages(“msigdbr”)
  • install.packages(“digest”)
  • install.packages(“rmarkdown”)
  • install.packages(“kable”)
  • BiocManager::install(“EBImage”)
  • install.packages(‘scattermore’)
  • install.packages(‘pbapply’)
  • install.packages(‘plotrix’)
  • install.packages(‘ggtext’)
  • install.packages(‘RBioFormats’)
  • BiocManager::install(“aoles/RBioFormats”)
  • install.packages(“C:/Users/inijman/Downloads/SpatialOmicsOverlay-0.99.13-beta.tar.gz”, dependencies = TRUE, repos = NULL)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“clusterProfiler”)
  • BiocManager::install(“pathview”)
#load libraries

#stack_size <- getOption("pandoc.stack.size", default = "512m")
#options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m, -XX:MetaspaceSize=1024M"))
options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m"))
library(NanoStringNCTools)
library(GeomxTools)
library(GeoMxWorkflows)
library(SpatialDecon)
library(GSVA) #for pathway analyses
library(msigdbr) #for molecular signatures in pathway analyses
library(knitr)
library(dplyr)
library(ggforce)
library(ggplot2)
library(scales) # for percent
library(reshape2)  # for melt
library(cowplot)   # for plot_grid
library(umap)
library(Rtsne)
library(pheatmap)  # for pheatmap
library(ggrepel) 
library(scales) #for ggplot peaudolog to prevent errors on log(0)
library(DT)
library(plotly)
library(gridExtra)
library(RColorBrewer)
library(SpatialOmicsOverlay)
library(gt)
library(tidyverse)
library(clusterProfiler)
library(fgsea)
library(pathview)
library(png)
library(readxl)
library(viridis)

library(standR)
library(SpatialExperiment)
library(limma)
library(ggalluvial)
library(scater)

#BiocManager::install("sva")
library(sva)

#BiocManager::install("preprocessCore") #quantile norm
library(preprocessCore)

#install.packages("Polychrome") #Get colors
library(Polychrome)


library(infercnv) # CNV
library("ape")
library("Biostrings")
library("ggtree")
library(tidytree)

1 loading base files

# Reference the main folder 'file.path' containing the sub-folders with each data file type:
datadir<-file.path("L:/pkloosterman/Github/DKD_Kidney/")

To locate a specific file path replace the above line with datadir <- file.path(“~/Folder/SubFolder/DataLocation”) replace the Folder, SubFolder, DataLocation as needed. The DataLocation folder should contain a dccs, pkcs, and annotation folder with each set of files present as needed automatically list files in each directory for use.

Take care you import a column with nuclei count separately if you want.

DCCFiles <- dir(file.path(datadir, "dccs"), pattern = ".dcc$",
                full.names = TRUE, recursive = TRUE)
PKCFiles <- dir(file.path(datadir, "pkcs"), pattern = ".pkc$",
                full.names = TRUE, recursive = TRUE)
SampleAnnotationFile <-
  dir(file.path(datadir, "annotation"), pattern = "^[^~]",
      full.names = TRUE, recursive = TRUE)

2 load data

Data <-
  readNanoStringGeoMxSet(dccFiles = DCCFiles,
                         pkcFiles = PKCFiles,
                         phenoDataFile = SampleAnnotationFile,
                         phenoDataSheet = "Template",
                         phenoDataDccColName = "Sample_ID",
                         protocolDataColNames = c("aoi", "roi"),
                         experimentDataColNames = c("panel"))

#save data to prevent loading time for retakes
#saveData<-Data
#Data<-saveData

#change Data column names and manual correction of spelling errors
Data@phenoData@data[["slide_name"]]<-Data@phenoData@data[["slide name"]]
Data@phenoData@data[["slide name"]]<-  NULL


#+1 references the slide name column
ann_size<-length(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))])+1 
ann_names<-c(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))],"slide_name")

# Feel free to change the order of which colors are appointed.
color<-c("#A349A4", "#FFFF33", "#E7298A", "#091833", "#1B9E77", "#D95F02", "#7570B3",  "#66A61E", "#E6AB02", "#8DD3C7", "#9F000F", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F", "#377EB8", "#984EA3", "#4DAF4A", "#FF71CE", "#FF7F00", "#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928")

# Use count and count_max to set the range of color needed for each column.
# With setNames() the color code is linked to each unique individual value of each column.
count=1
color_list = list()
for (ann in ann_names) {
  count_max = count+length(unique(Data@phenoData@data[[ann]]))-1
  color_list[[ann]]<-setNames(color[count:count_max], unique(Data@phenoData@data[[ann]]))
  count=count_max+1
}
value_names = c()
for (ann in ann_names) {
  value_names <- c(value_names, unique(Data@phenoData@data[[ann]]))
}

color_df <- as.data.frame(value_names)
color_df$color <- color[0:length(value_names)]

color_df %>% 
  mutate(color = fct_inorder(color)) |> 
  gt() %>% 
  data_color(columns = color, colors = as.character(color)) %>%
  tab_options(container.height = 500)
value_names color
DKD #A349A4
normal #FFFF33
disease3 #E7298A
disease4 #091833
normal3 #1B9E77
normal4 #D95F02
disease1B #7570B3
disease2B #66A61E
normal2B #E6AB02
glomerulus #8DD3C7
tubule #9F000F
abnormal #BEBADA
NA #FB8072
normal #80B1D3
niormal #FDB462
disease3 #B3DE69
disease4 #FCCDE5
normal3 #D9D9D9
normal4 #BC80BD
disease1B #CCEBC5
disease2B #FFED6F
normal2B #377EB8
paste("Reads from following runs used: ",unique(pData(protocolData(Data))$SeqSetId))
## [1] "Reads from following runs used:  "

3 Study design

pkcs <- annotation(Data)
modules <- gsub(".pkc", "", pkcs)
kable(data.frame(PKCs = pkcs, modules = modules))
PKCs modules
TAP_H_WTA_v1.0.pkc TAP_H_WTA_v1.0

Select the annotations we want to show, use `` to surround column names with spaces or special symbols

#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,slide_name)
count_mat <- pData(Data) %>% dplyr::count(pData(Data)[c(ann_names)])

Simplify the slide names if required

# count_mat$slide_name <- gsub("disease", "d", gsub("normal", "n", count_mat$slide_name))
#count_mat$path_ann <- gsub("i", "", count_mat$path_ann) #correcting spelling error

Gather the data and plot in order: class, slide name, region, segment

test_gr <- gather_set_data(count_mat, 1:all_of(ann_size))
test_gr$x <- factor(test_gr$x, labels = ann_names)

Plot Sankey

ggplot(test_gr, height = 10, width = 10, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 12) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  labs(x = "", y = "") +
  scale_fill_manual(values=color_list$ANN2) +
  annotate(geom = "segment", x = 4.25, xend = 4.25,
           y = 10, yend = 61, lwd = 2) +
  annotate(geom = "text", x = 4.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4 QC & Pre-processing

Shift counts to one

#shift any expression counts with a value of 0 to 1 to enable in downstream transformations.
Data <- shiftCountsOne(Data, useDALogic = TRUE)

4.1 Segment QC

We first assess sequencing quality and adequate tissue sampling for every ROI/AOI segment.

Every ROI/AOI segment will be tested for:

Raw sequencing reads: segments with >1000 raw reads are removed. % Aligned,% Trimmed, or % Stitched sequencing reads: segments below ~80% for one or more of these QC parameters are removed. % Sequencing saturation ([1-deduplicated reads/aligned reads]%): segments below ~50% require additional sequencing to capture full sample diversity and are not typically analyzed until improved. Negative Count: this is the geometric mean of the several unique negative probes in the GeoMx panel that do not target mRNA and establish the background count level per segment; segments with low negative counts (1-10) are not necessarily removed but may be studied closer for low endogenous gene signal and/or insufficient tissue sampling. No Template Control (NTC) count: values >1,000 could indicate contamination for the segments associated with this NTC; however, in cases where the NTC count is between 1,000- 10,000, the segments may be used if the NTC data is uniformly low (e.g. 0-2 counts for all probes). Nuclei: >100 nuclei per segment is generally recommended; however, this cutoff is highly study/tissue dependent and may need to be reduced; what is most important is consistency in the nuclei distribution for segments within the study. Area: generally correlates with nuclei; a strict cutoff is not generally applied based on area.

4.1.1 Select Segment QC

First, we select the QC parameter cutoffs, against which our ROI/AOI segments will be tested and flagged appropriately. We have selected the appropriate study-specific parameters for this study. Note: the default QC values recommended above are advised when surveying a new dataset for the first time.

Default QC cutoffs are commented in () adjacent to the respective parameters study-specific values were selected after visualizing the QC results in more detail below

QC_params <-
  list(minSegmentReads = 1000, # Minimum number of reads (1000)
       percentTrimmed = 80,    # Minimum % of reads trimmed (80%)
       percentStitched = 80,   # Minimum % of reads stitched (80%)
       percentAligned = 75,    # Minimum % of reads aligned (80%)
       percentSaturation = 50, # Minimum sequencing saturation (50%)
       minNegativeCount = 1,   # Minimum negative control counts (10)
       maxNTCCount = 9000,     # Maximum counts observed in NTC well (1000)
       minNuclei = 20,        # Minimum # of nuclei estimated (100)
       minArea = 1000)         # Minimum segment area (5000)
Data <-
  setSegmentQCFlags(Data, qcCutoffs = QC_params)        

cat("pre-QC features:", dim(Data)[1], "\npre-QC samples:", dim(Data)[2])
## pre-QC features: 18642 
## pre-QC samples: 276
#Table for clarification
QCparams_df <- data.frame (
  items = c("minSegmentReads","percentTrimmed","percentStitched","percentAligned","percentSaturation",
            "minNegativeCount","maxNTCCount","minNuclei","minArea"),
  defaults = c(1000,80,80,80,50,10,1000,100,5000),
  actual = c(QC_params$minSegmentReads,QC_params$percentTrimmed,QC_params$percentStitched,QC_params$percentAligned,QC_params$percentSaturation,QC_params$minNegativeCount,QC_params$maxNTCCount,QC_params$minNuclei,QC_params$minArea)
)

datatable(QCparams_df, rownames=FALSE,
          caption = "QC thresholds",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Collate QC Results

QCResults <- protocolData(Data)[["QCFlags"]]
flag_columns <- colnames(QCResults)
QC_Summary <- data.frame(Pass = colSums(!QCResults[, flag_columns]),
                         Warning = colSums(QCResults[, flag_columns]))

QCResults$QCStatus <- apply(QCResults, 1L, function(x) {
  ifelse(sum(x) == 0L, "PASS", "WARNING")
})

QC_Summary["TOTAL FLAGS", ] <-
  c(sum(QCResults[, "QCStatus"] == "PASS"),
    sum(QCResults[, "QCStatus"] == "WARNING"))

col_by <- "ANN1"
col_by_plate <- "Plate_ID"

4.2 Graphical summaries of QC statistics

Use the tab-menu to navigate!

QC_histogram <- function(assay_data = NULL,
                         annotation = NULL,
                         fill_by = NULL,
                         thr = NULL,
                         scale_trans = NULL) {
  plt <- ggplot(assay_data,
                aes_string(x = paste0("unlist(`", annotation, "`)"),
                           fill = fill_by)) +
    geom_histogram(bins = 50) +
    geom_vline(xintercept = thr, lty = "dashed", color = "black") +
    theme_bw() + guides(fill = "none") +
    facet_wrap(as.formula(paste("~", fill_by)), nrow = 4) +
    scale_fill_manual(values=color_list$ANN1) +
    labs(x = annotation, y = "segments, #", title = annotation)
  if(!is.null(scale_trans)) {
    plt <- plt +
      scale_x_continuous(trans = scale_trans)
  }
  plt
}

Trimmed

QC_histogram(sData(Data), "Trimmed (%)", col_by, QC_params$percentTrimmed)

Stiched (%)

QC_histogram(sData(Data), "Stitched (%)", col_by, QC_params$percentStitched)

Aligned (%)

QC_histogram(sData(Data), "Aligned (%)", col_by,QC_params$percentAligned)

Sequencing Saturation (%)

QC_histogram(sData(Data), "Saturated (%)", col_by, QC_params$percentSaturation) +
  labs(title = "Sequencing Saturation (%)",
       x = "Sequencing Saturation (%)")

Area

QC_histogram(sData(Data), "area", col_by, QC_params$minArea, scale_trans = "log10")

Nuclei count

QC_histogram(sData(Data), "nuclei", col_by, QC_params$minNuclei)

DuplicationRate

ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = (DeduplicatedReads/Raw))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_continuous(labels = scales::percent) +
  theme_bw()

Negprobes vs Endogenous

tmp_target_Data <- aggregateCounts(Data)

#get negative probe data
negs<-subset(tmp_target_Data,CodeClass=="Negative")

p1<-ggplot(pData(negs),
       aes(x = ANN2, fill = ANN2,
          y = assayDataElement(negs, elt = "exprs"))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Negative probes Expression") +
  scale_y_continuous(limits = c(1,3000), trans = "log2") +
  theme_bw() + coord_flip()


# get endogenous probe data
end<-subset(tmp_target_Data,CodeClass=="Endogenous")

p2<-ggplot(pData(end),
       aes(x = ANN2, fill = ANN2,
           y = colMeans(assayDataElement(end, elt = "exprs")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Endogenous probes Expression (mean)") +
  scale_y_continuous(limits = c(1,3000),trans = "log2") +
  theme_bw() + coord_flip()

pl <-list(p1,p2)
plot_grid(plotlist=pl, nrow=2, align='v')

Neg_probe reads compared to raw_reads

# make background total neg probe count
fdata_df<-fData(Data)
negprobesnames<-rownames(fdata_df[fdata_df$Negative==TRUE,])
temp_exp<-assayDataElement(Data,elt='exprs')
negprobe_expr_fd<-temp_exp[rownames(temp_exp) %in% negprobesnames,]
tot_neg_ctrl_reads<-colSums(negprobe_expr_fd)
tot_dedup_reads<-pData(protocolData(Data))$DeduplicatedReads

df<-data.frame('aoi'= names(tot_neg_ctrl_reads),'tot_dedup_reads' = as.numeric(tot_dedup_reads),'tot_neg_ctrl_reads'=as.numeric(tot_neg_ctrl_reads))
df<-melt(df,id="aoi")
ggplot(df,aes(fill=variable,y=value,x=aoi)) + 
  geom_bar(position="identity",stat="identity") +
  scale_y_continuous(trans = log2_trans()) +
  theme(legend.position="bottom",axis.text.x = element_blank(),axis.ticks.x=element_blank())                     

Duplicated reads vs Background

# get dcc per plate. sum negprobe counts/dcc/plate
ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = DeduplicatedReads)) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_log10()+
  geom_hline(data =pData(protocolData(Data)) , 
           aes(yintercept = NTC, colour=Plate_ID)) +
  theme_bw()

Duplicated reads vs ROIarea

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/area) )) +
  geom_point() +
  ylim(0,20) + 
  labs(y = "Deduplicated reads / ROI area") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

Duplicated reads vs nuclei

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/nuclei) )) +
  geom_point() +
  ylim(0,50) +                                                      # Adjust per project
  labs(y = "Deduplicated reads / nuclei") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

4.3 Process Negative GeoMeans

# Calculate the negative geometric means for each module
# It will show only the negative probes geomean, so expect less segments.
negativeGeoMeans <- 
  esBy(negativeControlSubset(Data), 
       GROUP = "Module", 
       FUN = function(x) { 
         assayDataApply(x, MARGIN = 2, FUN = ngeoMean, elt = "exprs") 
       }) 
protocolData(Data)[["NegGeoMean"]] <- negativeGeoMeans

negCols <- paste0("NegGeoMean_", modules)
pData(Data)[, negCols] <- sData(Data)[["NegGeoMean"]]
for(ann in negCols) {
  plt <- QC_histogram(pData(Data), ann, col_by, 2, scale_trans = "log10")
  print(plt)
}

# Detatch neg_geomean columns ahead of aggregateCounts call

pData(Data) <- pData(Data)[, !colnames(pData(Data)) %in% negCols]

Show all NTC values, Freq = # of Segments with a given NTC count:

QC<-sData(Data)

ntc_df <-QC[,c("slide_name","Plate_ID","NTC_ID","NTC")]
temptable<-ntc_df %>% dplyr::count(ntc_df$slide_name,ntc_df$NTC_ID,ntc_df$Plate_ID,ntc_df$NTC)
colnames(temptable) <- c("Slide_name","NTC_ID","Plate_ID","NTC_count","Number_of_samples")
datatable(temptable, rownames = FALSE)
kable(table(NTC_Count = sData(Data)$NTC), col.names = c("NTC Count", "# of Segments"))
NTC Count # of Segments
3 64
113 71
397 47
8704 94
kable(QC_Summary, caption = "QC Summary Table for each Segment")
QC Summary Table for each Segment
Pass Warning
LowReads 272 4
LowTrimmed 276 0
LowStitched 273 3
LowAligned 266 10
LowSaturation 272 4
LowNegatives 276 0
HighNTC 276 0
LowNuclei 276 0
LowArea 265 11
TOTAL FLAGS 259 17
datatable(QC_Summary,
          caption = "AOI QC Summary",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Show AOIs which fail critical QCs.

QC<-sData(Data)
undersat<-subset(QC, `Saturated (%)`<= QC_params$percentSaturation)

if(nrow(undersat)> 0) {

datatable(aggregate(undersat, by=list(undersat$SampleID),paste,collapse=";"),
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)}

Subsetting our dataset has removed samples which did not pass QC

Data <- Data[, QCResults$QCStatus == "PASS"]

Generally keep the qcCutoffs parameters unchanged. Set removeLocalOutliers to FALSE if you do not want to remove local outliers

Data <- setBioProbeQCFlags(Data, 
                               qcCutoffs = list(minProbeRatio = 0.1,
                                                percentFailGrubbs = 20), 
                               removeLocalOutliers = FALSE)

ProbeQCResults <- fData(Data)[["QCFlags"]]

Define QC table for Probe QC

qc_df <- data.frame(Passed = sum(rowSums(ProbeQCResults[, -1]) == 0),
                    Global = sum(ProbeQCResults$GlobalGrubbsOutlier),
                    Local = sum(rowSums(ProbeQCResults[, -2:-1]) > 0
                                & !ProbeQCResults$GlobalGrubbsOutlier))

Subset object to exclude all that did not pass Ratio & Global testing

ProbeQCPassed <- 
  subset(Data, 
         fData(Data)[["QCFlags"]][,c("LowProbeRatio")] == FALSE &
           fData(Data)[["QCFlags"]][,c("GlobalGrubbsOutlier")] == FALSE)

Data <- ProbeQCPassed 
cat("After QC features:", dim(Data)[1], "\nAfter QC samples:", dim(Data)[2])
## After QC features: 18641 
## After QC samples: 259

Check how many unique targets the object has

length(unique(featureData(Data)[["TargetName"]]))
## [1] 18504

Collapse to targets

target_Data <- aggregateCounts(Data)

exprs(target_Data)[1:5, 1:2]
##       DSP-1001250007851-H-A02.dcc DSP-1001250007851-H-A03.dcc
## A2M                           485                         262
## NAT2                           15                          18
## ACADM                          31                          15
## ACADS                          27                          17
## ACAT1                          29                          24

Define LOQ SD threshold and minimum value

cutoff <- 2
minLOQ <- 2

4.4 Limit of Quantification

We define a limit of quantification (LOQ) per ROI/AOI segment based on the negative control probes to guide the filtering of segments and genes with low signal relative to background. The formula for calculating the LOQ in the \(i^{th}\) segment at \(n\) standard deviations (\(n = 2\) for this study) is: \(LOQ_i=geomean(NegProbe_i)*geoSD(NegProbe_i)^n\)

Calculate LOQ per module tested

LOQ <- data.frame(row.names = colnames(target_Data))
for(module in modules) {
  vars <- paste0(c("NegGeoMean_", "NegGeoSD_"),
                 module)
  if(all(vars[1:2] %in% colnames(pData(target_Data)))) {
    LOQ[, module] <-
      pmax(minLOQ,
           pData(target_Data)[, vars[1]] * 
             pData(target_Data)[, vars[2]] ^ cutoff)
  }
}
pData(target_Data)$LOQ <- LOQ

4.5 Filtering

After determining the limit of quantification (LOQ) per segment, we recommend filtering out either segments and/or genes with abnormally low signal. Filtering is an important step to focus on the true biological data of interest.

We determine the number of genes detected in each segment across the dataset.

LOQ_Mat <- c()
for(module in modules) {
  ind <- fData(target_Data)$Module == module
  Mat_i <- t(esApply(target_Data[ind, ], MARGIN = 1,
                     FUN = function(x) {
                       x > LOQ[, module]
                     }))
  LOQ_Mat <- rbind(LOQ_Mat, Mat_i)
}
# ensure ordering since this is stored outside of the geomxSet
LOQ_Mat <- LOQ_Mat[fData(target_Data)$TargetName, ]

4.5.1 Segment Gene Detection

We first filter out segments with exceptionally low signal. These segments will have a small fraction of panel genes detected above the LOQ relative to the other segments in the study. Let’s visualize the distribution of segments with respect to their % genes detected:

Save detection rate information to pheno data

pData(target_Data)$GenesDetected <- 
  colSums(LOQ_Mat, na.rm = TRUE)
pData(target_Data)$GeneDetectionRate <-
  pData(target_Data)$GenesDetected / nrow(target_Data)

Determine detection thresholds: 1%, 5%, 10%, 15%, >15%

pData(target_Data)$DetectionThreshold <- 
  cut(pData(target_Data)$GeneDetectionRate,
      breaks = c(0, 0.01, 0.05, 0.1, 0.15, 0.2,1),
      labels = c("<1%", "1-5%", "5-10%", "10-15%", "15-20%", ">20%"))

# stacked bar plot of different cut points (1%, 5%, 10%, 15%)
ggplot(pData(target_Data),
       aes(x = DetectionThreshold)) +
  geom_bar(aes(fill = ANN2)) +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  theme_bw() +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "Gene Detection Rate",
       y = "Segments, #",
       fill = "Segment Type")

cut percent genes detected at 1, 5, 10, 15

kable(table(pData(target_Data)$DetectionThreshold,
            pData(target_Data)$ANN2))
disease1B disease2B disease3 disease4 normal2B normal3 normal4
<1% 0 0 0 0 1 0 0
1-5% 0 1 0 0 6 0 0
5-10% 8 3 0 1 5 1 0
10-15% 13 11 1 3 2 3 0
15-20% 7 5 3 7 5 10 0
>20% 5 11 55 13 15 41 23
# set threshold for detectionlevel
# default 0.1
gene_det_threshold <- 0.05

target_Data <-
  target_Data[, pData(target_Data)$GeneDetectionRate >= gene_det_threshold]

dim(target_Data)
## Features  Samples 
##    18504      251

4.6 Manual removal of samples/classes

active_aois<-names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

re-Collect annotations

# gather the data and plot in order: class, slide name, region, segment
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,ANN3,ANN4,slide_name)

temp_qc <- temp_df
temp_qc$QCResult <- QCResults$QCStatus
temp_qc$QCResult[temp_qc$QCResult == "WARNING"] <- "X"

#count_mat <- dplyr::count(a, ANN1, ANN2, slide_name, QCResult)
count_mat <- temp_qc %>% dplyr::count(temp_qc[c(ann_names, "QCResult")])

test_gr <- gather_set_data(count_mat, 1:(ann_size+1))
test_gr$x <- factor(test_gr$x, labels = c(ann_names, "QCResult"))

re-Plot Sankey

ggplot(test_gr, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 17) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "", y = "") +
  annotate(geom = "segment", x = 3.25, xend = 3.25, y = 10, 
           yend = 60, lwd = 2) +
  annotate(geom = "text", x = 3.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4.7 Gene Detection Rate

Calculate detection rate

LOQ_Mat <- LOQ_Mat[, colnames(target_Data)]
fData(target_Data)$DetectedSegments <- rowSums(LOQ_Mat, na.rm = TRUE)
fData(target_Data)$DetectionRate <-
  fData(target_Data)$DetectedSegments / nrow(pData(target_Data))

Gene of interest detection table

goi <- c("PDCD1", "CD274", "IFNG", "CD8A", "CD68", "EPCAM",
         "KRT18", "NPHS1", "NPHS2", "CALB1", "CLDN8")
goi_df <- data.frame(
  Gene = goi,
  Number = fData(target_Data)[goi, "DetectedSegments"],
  DetectionRate = percent(fData(target_Data)[goi, "DetectionRate"]))

4.8 Gene Filtering

We will graph the total number of genes detected in different percentages of segments. Based on the visualization below, we can better understand global gene detection in our study and select how many low detected genes to filter out of the dataset. Gene filtering increases performance of downstream statistical tests and improves interpretation of true biological signal.

Plot detection rate

plot_detect <- data.frame(Freq = c(1, 5, 10, 20, 30, 50))
plot_detect$Number <-
  unlist(lapply(c(0.01, 0.05, 0.1, 0.2, 0.3, 0.5),
                function(x) {sum(fData(target_Data)$DetectionRate >= x)}))
plot_detect$Rate <- plot_detect$Number / nrow(fData(target_Data))
rownames(plot_detect) <- plot_detect$Freq

ggplot(plot_detect, aes(x = as.factor(Freq), y = Rate, fill = Rate)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = formatC(Number, format = "d", big.mark = ",")),
            vjust = 1.6, color = "black", size = 4) +
  scale_fill_gradient2(low = "orange2", mid = "lightblue",
                       high = "dodgerblue3", midpoint = 0.65,
                       limits = c(0,1),
                       labels = scales::percent) +
  theme_bw() +
  scale_y_continuous(labels = scales::percent, limits = c(0,1),
                     expand = expansion(mult = c(0, 0))) +
  labs(x = "% of Segments",
       y = "Genes Detected, % of Panel > LOQ")

Subset to target genes detected in at least 10% of the samples. Also manually include the negative control probe, for downstream use

# default=0.1
negativeProbefData <- subset(fData(target_Data), CodeClass == "Negative")
neg_probes <- unique(negativeProbefData$TargetName)
target_Data <- 
  target_Data[fData(target_Data)$DetectionRate >= 0.05 |
                    fData(target_Data)$TargetName %in% neg_probes, ]

# retain only detected genes of interest
goi <- goi[goi %in% rownames(target_Data)]

dim(target_Data)
## Features  Samples 
##    12241      251

5 Normalization

We will now normalize the GeoMx data for downstream visualizations and differential expression. The two common methods for normalization of DSP-NGS RNA data are i) quartile 3 (Q3) or ii) background normalization.

Both of these normalization methods estimate a normalization factor per segment to bring the segment data distributions together. More advanced methods for normalization and modeling are under active development. However, for most studies, these methods are sufficient for understanding differences between biological classes of segments and samples.

Q3 normalization is typically the preferred normalization strategy for most DSP-NGS RNA studies. Given the low negative probe counts in this particular dataset as shown during Segment QC, we would further avoid background normalization as it may be less stable.

Before normalization, we will explore the relationship between the upper quartile (Q3) of the counts in each segment with the geometric mean of the negative control probes in the data. Ideally, there should be a separation between these two values to ensure we have stable measure of Q3 signal. If you do not see sufficient separation between these values, you may consider more aggressive filtering of low signal segments/genes.

Graph Q3 value vs negGeoMean of Negatives

ann_of_interest <- "ANN2"
Stat_data <- 
  data.frame(row.names = colnames(exprs(target_Data)),
             Segment = colnames(exprs(target_Data)),
             Annotation = pData(target_Data)[, ann_of_interest],
             Q3 = unlist(apply(exprs(target_Data), 2,
                               quantile, 0.75, na.rm = TRUE)),
             NegProbe = exprs(target_Data)[neg_probes, ])
Stat_data_m <- melt(Stat_data, measure.vars = c("Q3", "NegProbe"),
                    variable.name = "Statistic", value.name = "Value")

plt1 <- ggplot(Stat_data_m,
               aes(x = Value, fill = Statistic)) +
  geom_histogram(bins = 40) + theme_bw() +
  scale_x_continuous(trans = "log2") +
  facet_wrap(~Annotation, nrow = 1) + 
  scale_fill_brewer(palette = 3, type = "qual") +
  labs(x = "Counts", y = "Segments, #")

plt2 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3, color = Annotation)) +
  geom_abline(intercept = 0, slope = 1, lty = "dashed", color = "darkgray") +
  geom_point() + guides(color = "none") + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3 Value, Counts")

plt3 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3 / NegProbe, color = Annotation)) +
  geom_hline(yintercept = 1, lty = "dashed", color = "darkgray") +
  geom_point() + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3/NegProbe Value, Counts")

btm_row <- plot_grid(plt2, plt3, nrow = 1, labels = c("B", ""),
                     rel_widths = c(0.43,0.57))
plot_grid(plt1, btm_row, ncol = 1, labels = c("A", ""))

Q3 norm (75th percentile) for WTA/CTA with or without custom spike-ins

target_Data <- normalize(target_Data ,
                             norm_method = "quant", 
                             desiredQuantile = .75,
                             toElt = "q_norm")
#, data_type = "RNA" depricated after 4.1

Quantile Normalization

quantile <- normalize.quantiles(target_Data@assayData[["exprs"]])
rownames(quantile) <- rownames(target_Data@assayData[["exprs"]])
colnames(quantile) <- colnames(target_Data@assayData[["exprs"]])

assayDataElement(object = target_Data, elt = "quantile_norm") <-  quantile

Background normalization for WTA/CTA without custom spike-in

target_Data <- normalize(target_Data,
                             norm_method = "neg", 
                             fromElt = "exprs",
                             toElt = "neg_norm")

# , data_type = "RNA" depricated after 4.1

5.1 Visualize the first 10 segments with each normalization method

Use the tab-menu to navigate!

#Fix zero values, which go to -inf in log transform in standard boxplot
# temp <-as.matrix(exprs((target_Data)[,1:10]))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill="#9EDAE5") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Raw counts", x="segment", y = "Counts, Raw")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "q_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#2CA02C") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Q3 Norm Counts", x="segment", y = "Counts, Q3 Normalized")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "neg_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#FF7F0E") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Neg Norm Counts", x="segment", y = "Counts, Neg. Normalized")

raw counts

boxplot(exprs(target_Data)[,1:8],
        col = "#9EDAE5", main = "Raw Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Raw")

Q3 normalized

boxplot(assayDataElement(target_Data[,1:8], elt = "q_norm"),
        col = "#2CA02C", main = "Q3 Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Q3 Normalized")

Quantile normalized (testing phase)

boxplot(assayDataElement(target_Data[,1:8], elt = "quantile_norm"),
        col = "pink", main = "Quantile Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Quantile Normalized")

Negative probe normalization

boxplot(assayDataElement(target_Data[,1:8], elt = "neg_norm"),
        col = "#FF7F0E", main = "Neg Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Neg. Normalized")

6 Unsupervised Analysis

6.1 UMAP

Use the tab-menu to navigate!

1

custom_umap <- umap::umap.defaults
custom_umap$random_state <- 42
# run UMAP

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

batch_correction_needed = FALSE

2

ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = ANN3, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.2 Batch correction

If the data is observed to have a batch effect it can be corrected with the methods: RUV4, LIMMA, or Combat-Seq. Each method is done and the best one is picked and used.

The standR package is short for Spatial transcriptomics analyzes and decoding in R, it aims at providing good practice pipeline and useful functions for users to analyze Nanostring’s GeoMx DSP data. In the Nanostring’s GeoMX DSP protocol, due to the fact that one slide is only big enough for a handful of tissue segments (ROIs), it is common that we see the DSP data being confounded by the batch effect introduced by different slides. In order to establish fair comparison between ROIs later on, it is necessary to remove this batch effect from the data. (https://bioconductor.org/packages/release/bioc/vignettes/standR/inst/doc/standR_introduction.html)

For RUV4 correction, the function is requiring 3 parameters other than the input object, including factors: the factor of interest, i.e. the biological variation we plan to keep; NCGs: the list of negative control genes detected using the function findNCGs; and k: is the number of unwanted factors to use, in the RUV documentation, it is suggest that we should use the smallest k once we don’t observe technical variation in the data.

Another option is set the parameter method to “Limma”, which uses the remove batch correction method from limma. In this mode, the function is requiring 2 parameters, including batch: a vector that indicating batches for all samples; and design: a design matrix which is generated by model.matrix, in the design matrix, all biologically-relevant factors should be included.

ComBat-seq is a batch effect adjustment tool for bulk RNA-seq count data. It is an improved model based on the popular ComBat, to address its limitations through novel methods designed specifically for RNA-Seq studies. ComBat-seq takes untransformed, raw count matrix as input. Same as ComBat, it requires a known batch variable. (https://github.com/zhangyuqing/ComBat-seq)

Set up standR object

count_geomx <- as.data.frame(target_Data@assayData[["exprs"]])

sample_geomx <- target_Data@phenoData@data
sample_geomx$roi <- target_Data@protocolData@data$roi
sample_geomx <- as.data.frame(sample_geomx)
sample_geomx$Sample_ID <- rownames(sample_geomx)
sample_geomx$SegmentDisplayName <- paste(sample_geomx$`scan name`, sample_geomx$roi, sample_geomx$segment, sep = " | ")
sample_geomx$ROICoordinateX <- 1
sample_geomx$ROICoordinateY <- 1


feature_geomx <- fData(Data)
feature_geomx <- feature_geomx[c("RTS_ID", "TargetName", "ProbeID", "Negative")]
feature_geomx <- as.data.frame(feature_geomx)
rownames(feature_geomx) <- NULL

matching <- sample_geomx$SegmentDisplayName[sample_geomx$Sample_ID %in% colnames(count_geomx)]
colnames(count_geomx) <- matching
count_geomx$TargetName <- rownames(count_geomx)
rownames(count_geomx) <- NULL

spe <- readGeoMx(count_geomx, sample_geomx, featureAnnoFile = feature_geomx, hasNegProbe = TRUE)

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()
colData(spe)$biology <- paste0(colData(spe)$regions)

See optimal k value for RUV4

spe <- findNCGs(spe, batch_name = "slide_name", top_n = 500)
findBestK(spe, maxK = 10, factor_of_int = "biology", NCGs = metadata(spe)$NCGs, factor_batch = "slide_name")

RUV4

ruv4 <- geomxBatchCorrection(spe, factors = "biology", 
                   NCGs = metadata(spe)$NCGs, k = 1)

plotPairPCA(ruv4, assay = 2, color = slide_name, shape = regions, title = "RUV4 removeBatch")

plotRLExpr(ruv4, assay = 2, color = `slide_name`) + ggtitle("RUV4 removeBatch")

Limma

limma <- geomxBatchCorrection(spe,
                       batch = colData(spe)$`slide_name`, method = "Limma",
                       design = model.matrix(~ 0 + ANN1 + regions, 
                                             data = colData(spe)))

plotPairPCA(limma, assay = 2, color = slide_name, shape = regions, title = "Limma removeBatch")

plotRLExpr(limma, assay = 2, color = slide_name) + ggtitle("Limma removeBatch")

CombatSeq

adjusted <- ComBat_seq(target_Data@assayData[["exprs"]], batch=target_Data@phenoData@data[["slide_name"]], group=target_Data@phenoData@data[["ANN2"]])
assayDataElement(object = target_Data, elt = "combat") <-  adjusted

umap_out2 <-
  umap(t(log2(assayDataElement(target_Data , elt = "combat"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out2$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw() + ggtitle("CombatSeq removeBatch")

Compare limma vs RUV4

spe_list <- list(spe, ruv4, limma)

plotClusterEvalStats(spe_list = spe_list,
                     bio_feature_name = "regions",
                     batch_feature_name = "slide_name",
                     data_names = c("Raw","RUV4","Limma"))

Add batch correction result to target_Data

neg_probes_save <- t(as.matrix(target_Data@assayData$q_norm["NegProbe-WTX",]))
rownames(neg_probes_save) <- "NegProbe-WTX"

# Depending on correct method, change the word "limma" to "ruv4" or vice versa.
limma <-limma@assays@data@listData[["logcounts"]]
limma <- rbind(limma, neg_probes_save)
colnames(limma) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "limma") <-  limma

ruv4 <-ruv4@assays@data@listData[["logcounts"]]
ruv4 <- rbind(ruv4, neg_probes_save)
colnames(ruv4) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "ruv4") <-  ruv4

Choose method

# choose method to replace q_norm or set it to ""
method <- ""

# replace q_norm with chosen method
if (!method == "") {
  # save orginal q_norm
  assayDataElement(object = target_Data, elt = "original") <-  assayDataElement(object = target_Data, elt = "q_norm")
  
  assayDataElement(object = target_Data, elt = "q_norm") <-  assayDataElement(object = target_Data, elt = method)
  print(paste("Batch correction method:", method))
} else {print("No batch correction needed")}
## [1] "No batch correction needed"

Umap after batch correction

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.3 Run tSNE

Use the tab-menu to navigate!

One common approach to understanding high-plex data is dimension reduction. Two common methods are UMAP and tSNE, which are non-orthogonally constrained projections that cluster samples based on overall gene expression. In this study, we see by either UMAP (from the umap package) or tSNE (from the Rtsne package)

1

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, shape = slide_name, color = ANN2)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

2

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, color = ANN3, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.4 Clustering high CV Genes

Another approach to explore the data is to calculate the coefficient of variation (\(CV\)) for each gene (\(g\)) using the formula \(CV_g=SD_g/mean_g\). We then identify genes with high CVs that should have large differences across the various profiled segments. This unbiased approach can reveal highly variable genes across the study.

We plot the results using unsupervised hierarchical clustering, displayed as a heatmap.

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data,
                         elt = "log_q", MARGIN = 1, calc_CV)
# show the highest CD genes and their CV values
sort(CV_dat, decreasing = TRUE)[1:5]
##   CAMK2N1    AKR1C1      AQP2       REN     GDF15 
## 0.6344192 0.5238995 0.4752667 0.4414027 0.4276390

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap genes in the top 3rd of the CV values

GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]

pheatmap(assayDataElement(target_Data[GOI, ], elt = "log_q"),
         scale = "row",
         cutree_cols = 3,
         cutree_rows = 2,
         show_rownames = FALSE, show_colnames = TRUE,
         border_color = NA,
         drop_levels = TRUE,
         clustering_method = "average",
         clustering_distance_rows = "correlation",
         clustering_distance_cols = "correlation",
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

assayDataElement(object = target_Data, elt = "log_q") <-  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
log_q <-as.data.frame(assayDataElement(target_Data, elt= "log_q"))
#batch <-as.data.frame(assayDataElement(target_Data, elt= "batch"))

6.5.0 Create subset of data

# determine AOIs to use
#active_aois<-rownames(ann)[ann$patient=="p4"]
active_aois<- names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

6.5.1 Clustering high CV genes for subset

Calculating CV values

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data[,active_aois],
                         elt = "log_q", MARGIN = 1, calc_CV)

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap on of subset, genes in the top 3rd of the CV values

# Identify genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI,active_aois ], elt = "log_q"),
        scale = "row",
        fontsize_row = 5,
        cutree_cols = 3,
        cutree_rows = 2,
        show_rownames = FALSE, show_colnames = TRUE,
        border_color = NA,
        clustering_method = "average",
        clustering_distance_rows = "correlation",
        clustering_distance_cols = "correlation",
        breaks = seq(-3, 3, 0.05),
        color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
       annotation_colors = color_list,
        annotation_col =
          pData(target_Data)[, ann_names])

7.1 Differential Expression

t-test

#gc()
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BY"
#options: "holm"       "hochberg"   "hommel"     "bonferroni" "BH"         "BY"         "fdr" 
counter=1

comps_df<-data.frame(comp='',val='')

for(region in unique(pData(target_Data)$ANN3)) {
  for (active_group1 in unique(pData(target_Data)$ANN1)) {
    for (active_group2 in unique(pData(target_Data)$ANN1)) {
      
      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(region, active_group1,active_group2)),collapse = "_")
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)
      
      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group1 & pData(target_Data)$ANN3==region]]
      group2<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group2 & pData(target_Data)$ANN3==region]]
      
      #run t_tests  
      results<-as.data.frame ( apply(log_q, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"
      
      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)
      
      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)
      
      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame(apply(log_q, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]))))
      colnames(fchanges)<-"FC"
      #paste("FC",active_group1," / ",active_group2)
      results<-cbind(results,fchanges)
      
      #add genenames
      results$Gene<-rownames(results)
      
      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 1"
      results$Color[results$raw_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) <1] <- "NS or FC < 1"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 1", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
      
      #vulcanoplot
      
      # pick top genes for either side of volcano to label
      # order genes for convenience:
      
      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[, 'Gene'][
                   order(results[, 'invert_P'], decreasing = TRUE)[1:20]],
                 results[, 'Gene'][order(results[, 'invert_P'], decreasing = FALSE)[1:20]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix
      
      # Graph results
      
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(raw_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(1, -1), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched in", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, (-1>FC| FC>1) & FDR < 0.05 & Gene %in% top_g),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 10) +
        theme(legend.position = "bottom") +
        ggtitle(paste("class: ", region," - ", test, mtc,"multitest corr"))
      
      #store tables for display later
      tables[[counter]]<-results
      
      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }
}
grid.arrange(grobs=plots,ncol=2)

#strangly does not appear in html output -> chucks are limited to one datatable per chuck. The htmltools are a way around this but does show them stacked in a box.
results_tables <- htmltools::tagList()
for (c in (2:counter-1)) {
  #Gene %in% GOI
  results_tables[[c]] <- datatable(subset(tables[[c]], Color == c("FDR < 0.001",  "P < 0.05")), 
           rownames=FALSE,
           extensions = c('Buttons'), options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ), height = 550,
           caption = paste("DE results ", labels[[c]]),filter='top') %>% formatRound(columns=c("raw_p_value","adj_p_value","FDR","FC"), digits=3)
  #cat('\n\n<!-- -->\n\n')
}            
results_tables
print("") # for layout of next section
## [1] ""

7.2 DE analysis with LMM

A common statistical approach is to use a linear mixed-effect model (LMM). The LMM allows the user to account for the subsampling per tissue; in other words, we adjust for the fact that the multiple regions of interest placed per tissue section are not independent observations, as is the assumption with other traditional statistical tests. The formulation of the LMM model depends on the scientific question being asked.

Overall, there are two flavors of the LMM model when used with GeoMx data: i) with and ii) without random slope.

When comparing features that co-exist in a given tissue section, a random slope is included in the LMM model. When comparing features that are mutually exclusive in a given tissue section the LMM model does not require a random slope.

Mostly, we use patient/sample as Random Intercept when they are combined on slides.

# convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(target_Data)[["slide"]] <- factor(pData(target_Data)[["slide_name"]])
#assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# run LMM:
# formula follows conventions defined by the lme4 package
lmm_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
    ind <- pData(target_Data)$ANN3 == region

    mixedOutmc <-
        mixedModelDE(target_Data[,ind],
                     elt = "log_q",
                     modelFormula = ~ testClass + (1 | slide),
                     groupVar = "testClass",
                     nCores = parallel::detectCores(),
                     multiCore = TRUE)

    # format results as data.frame
    r_test <- do.call(rbind, mixedOutmc["lsmeans", ])
    tests <- rownames(r_test)
    r_test <- as.data.frame(r_test)
    r_test$Contrast <- tests

    # use lapply in case you have multiple levels of your test factor to
    # correctly associate gene name with it's row in the results table
    r_test$Gene <-
        unlist(lapply(colnames(mixedOutmc),
                      rep, nrow(mixedOutmc["lsmeans", ][[1]])))
    r_test$Subset <- region
    r_test$FDR <- p.adjust(r_test$`Pr(>|t|)`, method = "fdr")
    r_test <- r_test[, c("Gene", "Subset", "Contrast", "Estimate",
                         "Pr(>|t|)", "FDR")]
    
    lmm_results <- rbind(lmm_results, r_test)
}

7.3 Vulcanoplot + table of LMM

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 1

lmm_results$Color <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color[lmm_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_results$Color[lmm_results$FDR < 0.05] <- "FDR < 0.05"
lmm_results$Color[lmm_results$FDR < 0.001] <- "FDR < 0.001"
lmm_results$Color[abs(lmm_results$Estimate) < fc_threshold] <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color <- factor(lmm_results$Color,
                        levels = c("NS or FC < 1", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))
counter = 1
plots_lmm <- list()
for(c in unique(lmm_results$Subset) ) {
  lmm_results_slice = lmm_results[lmm_results$Subset == c,]

  # pick top genes for either side of volcano to label
  # order genes for convenience:
  lmm_results_slice$invert_P <- (-log10(lmm_results_slice$`Pr(>|t|)`)) * sign(lmm_results_slice$Estimate)
  
  #loop here over tested conditions if applicable
  top_g <- c()
  top_g <- c(top_g,
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])

  top_g <- unique(top_g)
  
  lmm_results_slice <- lmm_results_slice[, -1*ncol(lmm_results_slice)] # remove invert_P from matrix
  
  # Flip Contrast
  contrast_lab <- as.character(lmm_results_slice$Contrast)
  contrast_lab <- strsplit(contrast_lab, "-")[[1]]
  contrast_lab <- paste(contrast_lab[2], "-", contrast_lab[1])
  
  # Graph results
  plots_lmm[[counter]] <- ggplot(lmm_results_slice,
               aes(x = Estimate, y = -log10(`Pr(>|t|)`),
                   color = Color, label = Gene)) +
          geom_vline(xintercept = c(fc_threshold, -fc_threshold), lty = "dashed") +
          geom_hline(yintercept = -log10(0.05), lty = "dashed") +
          geom_point() +
          labs(
            x = paste(contrast_lab, " log2(FC)"),
               y = "Significance, -log10(P)",
               color = "Significance") +
          scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                        `FDR < 0.05` = "lightblue",
                                        `P < 0.05` = "orange2",
                                        `NS or FC < 1` = "gray"),
                             guide = guide_legend(override.aes = list(size = 4))) +
          scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
          geom_text_repel(data = subset(lmm_results_slice,  `Pr(>|t|)` < 0.001 & abs(lmm_results_slice$Estimate) > fc_threshold & Gene %in% top_g),
                          point.padding = 0.15, color = "black",size=5,
                          min.segment.length = .1, box.padding = .2, lwd = 2,
                          max.overlaps = 50) +
          theme_bw(base_size = 16) +
          theme(legend.position = "bottom") +
          facet_wrap(~Subset, scales = "free_y")
  counter <- counter + 1
}
grid.arrange(grobs=plots_lmm,ncol=2)

#subset(lmm_results, Gene %in% GOI)
datatable(lmm_results, rownames = FALSE,
          extensions = 'Buttons', options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Genes of Interest (>75% CV)",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)

7.4 Plotting Genes of Interest

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)
tmp_tbl<-subset(lmm_results, Gene %in% my_gois)

my_gois <- c()
for (contrast in unique(tmp_tbl$Contrast)) { # gene of interest for all contrasts
#for (contrast in c("Jux_Glo - Pro_Tub")) {
  ind <- tmp_tbl$Contrast == contrast
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = FALSE)[1:3]]
  my_gois <- c(my_gois, goi)
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = TRUE)[1:3]]
  my_gois <- c(my_gois, goi)
}

if(nrow(tmp_tbl) > 1) { 
  datatable(tmp_tbl,rownames = FALSE,caption = "DE results for Genes of Interest ",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
 
for (my_goi in my_gois) {
# show expression for a single target
  a<-ggplot(pData(target_Data),
       aes(x = ANN2, fill = ANN2,
           y = as.numeric(assayDataElement(target_Data[my_goi, ], elt = "q_norm")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = paste(my_goi,"Expression")) +
  scale_y_continuous(trans = "log2") +
  #facet_wrap(~ANN3, nrow=1) + theme_bw(base_size = 16) +
  theme_bw() + coord_flip()
  print(a)
}
}else{
  print("No significant lMM results to plot")
}

7.5 Heatmap of Significant Genes

In addition to generating individual gene box plots or volcano plots, we can again create a heatmap from our data. This time rather than utilizing CV to select genes, we can use the P-value or FDR values to select genes. Here, we plot all genes with an FDR < 0.001.

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)   # 100 to prevent long runtime

if(length(my_gois)<2) {
  print("No significant results to show")
 
}else{

pheatmap(log2(assayDataElement(target_Data[my_gois, ], elt = "q_norm")),
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         border_color = NA,
         clustering_method = "average",
         clustering_distance_rows = "correlation",
         clustering_distance_cols = "correlation",
         cutree_cols = 3, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
}

8 Pathway Analysis

Pathway analysis enables exploration of different aggregate gene sets for our experimental questions. Each individual ROI/AOI segment is scored for every pathway of interest, which we can then use to investigate biological differences. We will perform analogous analyses as those outlined in the Differential Expression and Spatial Deconvolution sections of the report for gene set enrichment.

8.1 Scoring Gene Sets

Pathways and gene sets were defined from the Kegg Brite database. We use an R software package called Gene Set Variation Analysis to score each segment within our study. see https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html for options on collections. We use the KEGG and REACTOME.

#gc()
h_gene_sets = rbind(msigdbr(species = "human", subcategory = "CP:KEGG"),
                    msigdbr(species = "human", subcategory = "CP:REACTOME"))
#msigdbr(species = "human", subcategory = "CP:BIOCARTA")

msigdbr_list = split(x = h_gene_sets$gene_symbol, f = h_gene_sets$gs_name)

# prepare df for accurate merging back genes later
pw_gene_df<-data.frame(Pathway = names(msigdbr_list))
pw_gene_df$genes<-msigdbr_list
ssgsea_results <- GSVA::gsva(expr = assayDataElement(target_Data,
                            elt = "log_q"),
                            gset.idx.list = msigdbr_list,
                            method = "ssgsea",
                            min.sz = 5,
                            max.sz = 500,
                            verbose = FALSE)
## [1] "Calculating ranks..."
## [1] "Calculating absolute values from ranks..."
## [1] "Normalizing..."
geneSetObj <-
  NanoStringGeoMxSet(assayData = ssgsea_results,
                     phenoData = AnnotatedDataFrame(pData(target_Data)),
                     protocolData = protocolData(target_Data),
                     featureType = "GeneSet",
                     check = FALSE)

8.2 Differental analysis of pathways

# # convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(geneSetObj)[["slide"]]<-factor(pData(geneSetObj)[["slide_name"]])
#pData(target_Data)$testRegion<-factor(pData(target_Data)$ANN2, unique(count_mat$ANN3))

lmm_ssgsea_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
  ind <- pData(target_Data)$ANN3 == region
  mixedOutmc <-
    mixedModelDE(geneSetObj[, ind],
                 elt = "exprs",
                 modelFormula = ~ testClass + (1 | slide),
                 groupVar = "testClass",
                 nCores = parallel::detectCores(),
                 multiCore = TRUE)

  # format results as data.frame
  r_ssgsea_test <- do.call(rbind, mixedOutmc["lsmeans", ])
  ssgsea_tests <- rownames(r_ssgsea_test)
  r_ssgsea_test <- as.data.frame(r_ssgsea_test)
  r_ssgsea_test$Contrast <- ssgsea_tests
  #r_ssgsea_test$Genes <- msigdbr_list seems unreliable as gsva omits pathways if genes are not in data..

  # use lapply in case you have multiple levels of your test factor to
  # correctly associate gene name with it's row in the results table
  r_ssgsea_test$Pathway <-
    unlist(lapply(colnames(mixedOutmc),
                  rep, nrow(mixedOutmc["lsmeans", ][[1]])))

  #r_ssgsea_test$Subset <- status
  r_ssgsea_test$FDR <- p.adjust(r_ssgsea_test$`Pr(>|t|)`, method = "fdr")
  r_ssgsea_test$Subset <- region
  r_ssgsea_test <- r_ssgsea_test[, c("Pathway", "Subset", "Contrast", "Estimate",
                                     "Pr(>|t|)", "FDR")]
  lmm_ssgsea_results <- rbind(lmm_ssgsea_results, r_ssgsea_test)

  }
  lmm_ssgsea_results <- merge(lmm_ssgsea_results, pw_gene_df)

8.2.1 Table of Differental analysis of pathways

datatable(subset(lmm_ssgsea_results), rownames = FALSE,
          extensions = 'Buttons', options = list (
             pageLength = 10,
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Pathways",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=5)

8.3 Vulcanoplot of LMM_Pathways

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3

lmm_ssgsea_results$Color <- "NS or FC < 0.3"
lmm_ssgsea_results$Color[lmm_ssgsea_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.05] <- "FDR < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.001] <- "FDR < 0.001"
lmm_ssgsea_results$Color[abs(lmm_ssgsea_results$Estimate) < fc_threshold] <- "NS or FC < 0.3"
lmm_ssgsea_results$Color <- factor(lmm_ssgsea_results$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

# pick top pw for either side of volcano to label
# order pw for convenience:
lmm_ssgsea_results$invert_P <- (-log10(lmm_ssgsea_results$`Pr(>|t|)`)) * sign(lmm_ssgsea_results$Estimate)
top_ssgsea_g <- c()

#loop here over tested conditions if applicable

for(c in unique(lmm_ssgsea_results$Subset) ) {
  lmm_results_slice = lmm_ssgsea_results[lmm_ssgsea_results$Subset == c,]
 top_ssgsea_g <- c(top_ssgsea_g,
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
}
 
top_ssgsea_g <- unique(top_ssgsea_g)
lmm_ssgsea_results <- lmm_ssgsea_results[, -1*ncol(lmm_ssgsea_results)] # remove invert_P from matrix

#lmm_ssgsea_results_slice <- lmm_ssgsea_results_slice[lmm_ssgsea_results_slice$FDR < 1,]

# Graph results
ggplot(lmm_ssgsea_results,
       aes(x = Estimate, y = -log10(`Pr(>|t|)`),
           color = Color, label = Pathway)) +
    geom_vline(xintercept = c(0.2, -0.2), lty = "dashed") +
    geom_hline(yintercept = -log10(0.05), lty = "dashed") +
    geom_point() +
    labs(x = paste(lmm_results_slice$Contrast, "log2(FC)"),
         y = "Significance, -log10(P)",
         color = "Significance") +
    scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                  `FDR < 0.05` = "lightblue",
                                  `P < 0.05` = "orange2",
                                  `NS or FC < 0.5` = "gray"),
                       guide = guide_legend(override.aes = list(size = 4))) +
    scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
    geom_text_repel(data = subset(lmm_ssgsea_results, Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                   point.padding = 0.15, color = "black",size=3,
                   min.segment.length = .1, box.padding = .2, lwd = 2,
                   max.overlaps = 50) +
    theme_bw(base_size = 16) +
    theme(legend.position = "bottom") +
    facet_wrap(~Subset, scales = "free_y")

#    +facet_wrap(~Subset, scales = "free_y"))

8.4 heatmap of pathways

  active_pw<-filter(lmm_ssgsea_results, `Pr(>|t|)` < 0.001)$Pathway
  active_pw<-filter(lmm_ssgsea_results, FDR < 0.001 )$Pathway
  #active_pw<-filter(lmm_ssgsea_results, Color == "FDR < 0.001")$Pathway
  
  
  active_pw<-top_ssgsea_g
  
  if (length(active_pw)>1) {
  
    print("go")
    
  pw_matrix<-assayDataElement(geneSetObj, elt = "exprs")

  pheatmap(pw_matrix[active_pw,],
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         fontsize_row = 10,
         border_color = NA,
         clustering_method = "average",
         #clustering_distance_rows = "correlation",
         clustering_distance_cols = "euclidean",
         cutree_cols = 2, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         #color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         main = "Heatmap of selected Pathways",
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
  
  }else{
    print("No significant results to display")
  }
## [1] "go"

8.5 fgsea pathway analysis

Another option for pathway analysis is the R software package called Fast Gene Set Enrichment Analysis. It is a pre-ranked method that uses the LMM results combined with the same pathway list from msigdbr as GSVA.

fgsea_results_all <- c()
for (contrast in unique(lmm_results$Contrast)) {
    
    ranks <- lmm_results$Estimate[lmm_results$Contrast == contrast]
    names(ranks) <- lmm_results$Gene[lmm_results$Contrast == contrast]
    
    fgsea_results <- fgsea(msigdbr_list, 
                         ranks, 
                         eps = 0.0,
                         minSize=15, 
                         maxSize = 500)
    fgsea_results$Contrast <- contrast
    fgsea_results_all <- rbind(fgsea_results_all, fgsea_results)
    
}

fgsea_reactome <- fgsea_results_all[grep("REACTOME", pathway),]
fgsea_reactome$pathway <- gsub("REACTOME_", "", fgsea_reactome$pathway)
fgsea_reactome$pathway <- gsub("_", " ", fgsea_reactome$pathway)
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3


fgsea_results_all$Color <- "NS or FC < 0.3"
fgsea_results_all$Color[fgsea_results_all$pval < 0.05] <- "P < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.05] <- "FDR < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.001] <- "FDR < 0.001"
fgsea_results_all$Color[abs(fgsea_results_all$ES) < fc_threshold] <- "NS or FC < 0.3"
fgsea_results_all$Color <- factor(fgsea_results_all$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

plt <- htmltools::tagList()
counter = 1
for(c in unique(fgsea_results_all$Contrast) ) {
  fgsea_results_slice = fgsea_results_all[fgsea_results_all$Contrast == c,]

  # # pick top genes for either side of volcano to label
  # # order genes for convenience:
  fgsea_results_slice$invert_P <- (-log10(fgsea_results_slice$pval)) * sign(fgsea_results_slice$ES)
  
  # #loop here over tested conditions if applicable
  #top_fgsea_g <- c()
  top_fgsea_g <- c(fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
                    fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
   
  # for (celltype in unique(lmm_results$Subset)) {
  #      top_fgsea_g <- c(top_fgsea_g,
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:20]],
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:20]])
  # }
  
  top_fgsea_g <- unique(top_fgsea_g)
  #fgsea_results_slice <- fgsea_results_slice[, -1*ncol(fgsea_results_slice)] # remove invert_P from matrix
  
  # Graph results
  dynplot <- ggplot(fgsea_results_slice,
         aes(x = NES, y = -log10(pval),
             color = Color, label = pathway)) +
      geom_vline(xintercept = c(fc_threshold,-fc_threshold), lty = "dashed") +
      geom_hline(yintercept = -log10(0.05), lty = "dashed") +
      geom_point() +
      labs(x = paste(c, " FC"),
           y = "Significance, -log10(P)",
           color = "Significance") +
      scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                    `FDR < 0.05` = "lightblue",
                                    `P < 0.05` = "orange2",
                                    `NS or FC < 0.3` = "gray"),
                         guide = guide_legend(override.aes = list(size = 4))) +
      scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
      geom_text_repel(data = subset(fgsea_results_slice, Color == "P < 0.05" | Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                     point.padding = 0.15, color = "black",size=5,
                     min.segment.length = .1, box.padding = .2, lwd = 2,
                     max.overlaps = 50) +
      theme_bw(base_size = 16) +
      theme(legend.position = "bottom") #+
      #facet_wrap(~Subset, scales = "free_y")
   
  plt[[counter]] <- as_widget(ggplotly(dynplot))
  counter <- counter + 1
  #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
plt

8.6 Visualize KEGG pathway

Get pathway visualization from KEGG. clusterProfiler grabs the KEGG pathway IDs and combined with pathview it can download a pathway image from the KEGG database. The LMM results are given with the pathway id to visualize up and down regulated genes.

get_wp_gmtfile <- function() {
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    x <- readLines(wpurl)
    y <- x[grep('\\.gmt',x)]
    sub(".*(wikipathways-.*\\.gmt).*", "\\1",  y[grep('File', y)])
}

get_wp_data <- function(organism) {
    organism <- sub(" ", "_", organism)
    gmtfile <- get_wp_gmtfile()
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    url <- paste0(wpurl,
                  gmtfile[grep(organism, gmtfile)])
    f <- tempfile(fileext = ".gmt")
    dl <- mydownload(url, destfile = f)
    if (is.null(f)) {
        message("fail to download wikiPathways data...")
        return(NULL)
    }
    read.gmt.wp(f)
}
# enrichKEGG grab online pathways
gene <-  target_Data@featureData@data[["GeneID"]]
kk <- enrichKEGG(gene         = gene,
                 organism     = 'hsa',
                 pvalueCutoff = 0.1)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
enrichKEGG_results <- kk@result

# Match column values for link
# fsgea run
fgsea_results$Description <- gsub("KEGG_", "", fgsea_results$pathway)
fgsea_results$Description <- gsub("_", " ", fgsea_results$Description)
# ssgea run
# lmm_ssgsea_results_d$Description <- gsub("KEGG_", "", lmm_ssgsea_results_d$Pathway)
# lmm_ssgsea_results_d$Description <- gsub("_", " ", lmm_ssgsea_results_d$Description)

enrichKEGG_results$Description <- toupper(enrichKEGG_results$Description)

# Create df for matching KEGG results
KEGGIDs <- fgsea_results#[fgsea_results$Contrast == "Pro_Tub - Dis_Tub"]
KEGGIDs <- KEGGIDs %>% inner_join(enrichKEGG_results, by = 'Description') %>% select(Description, ID, geneID, leadingEdge, padj)

# Create geneList with DE results for visualization pathway genes 
genelist <- lmm_results$Estimate
names(genelist) <- target_Data@featureData@data[["GeneID"]]

# Choose manual or highest significant pathway
pathway_name <- "INSULIN SIGNALING PATHWAY" # manual pathway query
pathway_name <- toupper(pathway_name)
if (pathway_name == "") {
  pathway_name <- KEGGIDs[order(KEGGIDs$padj, decreasing = FALSE), ][1]$Description
}
pathwayid <- KEGGIDs$ID[KEGGIDs$Description == pathway_name]

# Grab pathway image with gene info, save and plot
viewPath <- pathview(gene.data  = genelist,
                     pathway.id = pathwayid,
                     species    = "hsa",
                     out.suffix = "genesinfo", kegg.native = T, same.layer = F)
## Info: Downloading xml files for hsa04910, 1/1 pathways..
## Info: Downloading png files for hsa04910, 1/1 pathways..
## 'select()' returned 1:1 mapping between keys and columns
## Info: Working in directory L:/pkloosterman/Github/DKD_Kidney
## Info: Writing image file hsa04910.genesinfo.png
img <- readPNG(paste(pathwayid, ".genesinfo.png", sep = ""))
grid::grid.raster(img)

Barplot pathways

Clear vizualization of the top 15 positive and negative expressed Reactome pathways. Pathways with a adjusted p-value of 0.05 or higher will be presented as red and below 0.05 as green.

top_all <- c()
for (contrast in unique(fgsea_reactome$Contrast)) {
    # active_group1 <- contrast_list[contrast][[1]][[1]]
    # active_group2 <- contrast_list[contrast][[1]][[2]]
    # ind <- pData(target_Data)[pData(target_Data)$ANN2 == active_group1 | pData(target_Data)$ANN2 == active_group2]
    top <- fgsea_reactome[fgsea_reactome$Contrast == contrast]
    toppos <- top[order(top$NES, decreasing = TRUE)][0:15]
    topneg <- top[order(top$NES, decreasing = FALSE)][0:15]
    top <- rbind(toppos, topneg)
    
    top$Color[top$padj < 0.05] <- "padj < 0.05"
    top$Color[top$padj == 0.05 | top$padj > 0.05] <- "padj > 0.05"
    top_all <- rbind(top_all, top)
    barplot <- ggplot(top,aes(x=reorder(pathway, NES),y=NES,fill=Color)) + 
      scale_x_discrete(labels = function(x) str_wrap(x, width = 100)) + 
      geom_col(position="dodge") + scale_fill_manual(values = c("#2ECC71", "#E74C3C")) +
      ggtitle(paste("Top 15 + and - NES reactome pathways from", contrast)) + 
      xlab("pathway") + ylab("Normalized Enrichment Score") +
      coord_flip() + 
      theme(text = element_text(size = 20)) +
      theme(panel.background = element_blank())
      #facet_wrap(~Contrast, scales = "free_y")
    print(barplot)
}

9 Spatial Deconvolution

9.1 Calculate backgrounds

#gc()
bg = derive_GeoMx_background(
  norm = assayDataElement(target_Data , elt = "q_norm"),
  probepool = fData(target_Data)$Module,
  negnames = c("NegProbe-CTP01","NegProbe-Kilo","Negative Probe", "NegProbe-WTX" ))
  #negnames = "NegProbe-WTX")

9.2 Load cell profile

A “cell profile matrix” is a pre-defined matrix that specifies the expected expression profiles of each cell type in the experiment. The SpatialDecon library comes with one such matrix pre-loaded, the “SafeTME” matrix, designed for estimation of immune and stroma cells in the tumor microenvironment. (This matrix was designed to avoid genes commonly expressed by cancer cells; see the SpatialDecon manuscript for details.). Otherwise, load specific profiles from https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

#safeTME
data("safeTME")
data("safeTME.matches")
current_cell_profile<-safeTME

#see: https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

current_cell_profile <- download_profile_matrix(species = "Human",
                                               age_group = "Adult",
                                               matrixname = "Kidney_HCA")

heatmap(sweep(current_cell_profile, 1, apply(current_cell_profile, 1, max), "/"),
        labRow = NA, margins = c(10, 5), cexCol = 0.7)

9.3 Run spatial deconvolution

# vector identifying pure tumor segments:
#target_Data$istumor = target_Data$ANN3 == "CORE" & target_Data$ANN1 == "PanCK+"
res = runspatialdecon(object = target_Data,
                      norm_elt = "q_norm",
                      raw_elt = "exprs",
                      #is_pure_tumor = target_Data$istumor,
                      cell_counts = target_Data$nuclei,
                      X = current_cell_profile,
                      #cellmerges = safeTME.matches,              # safeTME.matches object, used by default
                      #n_tumor_clusters = 5,                      # how many distinct tumor profiles to append to safeTME
                      align_genes = TRUE)

9.3.1 Spatial deconvolution heatmaps

Abundance

# NOTE: check clustering.. why different?

#set display thresholds
thresh <- signif(quantile(res$beta, 0.97), 2)

# plot stored to keep clustering for later
p1<-pheatmap(pmin(t(res$beta),thresh),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, thresh, length.out = 5))[-5], thresh),
         legend_labels = c(round(seq(0, thresh, length.out = 5))[-5], paste0("Abundance scores,\ntruncated above at ", thresh)),
         #breaks = seq(0, 5, 1),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names]
         )

#p1

Proportional

# proportions:
props <- replace(res$prop_of_nontumor, is.na(res$prop_of_nontumor), 0)

p2<-pheatmap(t(props),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = round(seq(0, max(props) * 0.99, length.out = 5), 2),
         legend_labels = c(round(seq(0, max(props), length.out = 5), 2)[-5], "Proportion of all\nfitted populations"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

#p2

Scaled

# scaled abundances:
epsilon <- min(res$beta[res$beta > 0])
mat <- sweep(res$beta, 1, pmax(apply(res$beta, 1, max), epsilon), "/")

pheatmap(t(mat),
         #scale = "row",
         cutree_cols = 3,
         cutree_rows = 3,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, 1, length.out = 5), 2)[-5], 1),
         legend_labels = c(round(seq(0, 1, length.out = 5), 2)[-5], "Scaled abundance\n(ratio to max)"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

9.4 Barplots

abundance

# define variables to show in heatmaps:
pData(target_Data)$region <- 
    factor(pData(target_Data)$ANN3, unique(target_Data$ANN3))   # Must be manual?
pData(target_Data)$class <- 
    factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))   # Must be manual?

variables_to_plot <- c("slide_name", "ANN1", "ANN3")                     # Must be manual?

#define colors

# only for safeTME colors
#col <- cellcols

#custom celmatrix
#get large number of colors
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unique(unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals))))
celltypes<-sample(col_vector,length(colnames(res$beta)))
names(celltypes)<-colnames(res$beta)
col<-celltypes


#tempfix for abbreviated and now mismatching annotations
#can just use ann dataframe?
#tmpann<-cbind(ANN1,ANN2,SN)
tmpann <- pData(target_Data)[ann_names]



layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p1$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$beta)[, p1$tree_col$order]
# infer scale of negative y-axis for annotation colorbars
ymin <- -max(colSums(mat)) * 0.15
if (!is.finite(ymin)) {
  ymin <- 0
}

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Abundance scores",
              ylim = c(ymin, max(colSums(mat)))
)


# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p1$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)

#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.5,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

proportional

# define variables to show in heatmaps:
variables_to_plot <- c("slide_name", "ANN1", "ANN3")

layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p2$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$prop_of_nontumor)[, p2$tree_col$order]
  mat <- replace(mat, is.na(mat), 0)
  # infer scale of negative y-axis for annotation colorbars
  ymin <- -0.15

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Proportion of fitted cells",
              ylim = c(ymin, max(colSums(mat)))
)

# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p2$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)


#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.4,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

11 CNV

Copy Number Variation analysis with the R software package inferCNV (https://github.com/broadinstitute/inferCNV/wiki).

InferCNV is used to explore tumor single cell RNA-Seq data to identify evidence for somatic large-scale chromosomal copy number alterations, such as gains or deletions of entire chromosomes or large segments of chromosomes. This is done by exploring expression intensity of genes across positions of tumor genome in comparison to a set of reference ‘normal’ cells. A heatmap is generated illustrating the relative expression intensities across each chromosome, and it often becomes readily apparent as to which regions of the tumor genome are over-abundant or less-abundant as compared to that of normal cells.

Per patient a cnv analysis with a provided reference group. The gene order is made from the projects .pkc file.

Select the annotation where the CNV analysis should look at specifically as group and specify a subgroup as group_filter if the group of interest is a subgroup inside the annotation. For example if the CNV analysis needs to be only on tumor regions the group would be ANNX(ANN column with the info about the tumor regions) and group_filter PanCK+. The annotation that references the patients as patients. If there are no patients leave the parameter empty as ““. The annotation that should be included in the results including a reference set as cnv_target. This will create patient specific files with all the information inferCNV needs.

Select the reference set in reference. This can be more than one. Every patient without its own reference will not be analysed.

#########PARAMETERS########
reference <- c("normal")
###########################

failed <- c()
cnv_list <- list()

for (patient in patient_list) {
  # Name output folder
  out_dir <- paste0(patient, "_CNV")
  if (str_detect(paste(readLines(paste0(patient, ".Annotations.tsv")), collapse = ''), reference) == FALSE) {
    failed <- c(failed, patient)
    next
    }

  # Create the infercnv object
  infercnv_obj = CreateInfercnvObject(raw_counts_matrix= paste0(patient, ".Counts.tsv"),
                                      annotations_file= paste0(patient, ".Annotations.tsv"),
                                      delim="\t",
                                      gene_order_file= "gene_order_Hs_WTA_v1_pkc.txt",
                                      #gene_order_file= "gencode_v19_gene_pos.txt",
                                      ref_group_names= reference, # input the normal/reference group names
                                      chr_exclude = c("chrM"))#c("chrM")) # Default excludes chrX, chrY and chrM. By only picking chrM you include the X and Y chromosomes.

  # perform infercnv operations to reveal cnv signal. For all options: https://rdrr.io/github/broadinstitute/infercnv/man/run.html
  #cnv_list[[patient]]
  infercnv_obj <- infercnv::run(infercnv_obj,
                               cutoff=0.1,  # use 1 for smart-seq, 0.1 for 10x-genomics
                               out_dir= out_dir,  # dir is auto-created for storing outputs
                               cluster_by_groups=FALSE,   # cluster
                               denoise=TRUE,
                               HMM=FALSE,
                               tumor_subcluster_partition_method = c("qnorm"),
                               analysis_mode = "subclusters",
                               no_plot=TRUE
                               #,debug=TRUE
                               )

  plot_cnv(infercnv_obj,
          out_dir = paste0(patient, "_CNV"),
          title = "inferCNV",
          obs_title = "Observations (Cells)",
          ref_title = "References (Cells)",
          cluster_by_groups = FALSE,
          cluster_references = FALSE,
          plot_chr_scale = FALSE,
          #chr_lengths = NULL,
          k_obs_groups = 1,
          contig_cex = 1.5,
          #x.center = mean(infercnv_obj@expr.data),
          x.range = "auto",
          #hclust_method = "ward.D",
          output_filename = "infercnv",
          output_format = "png",
          png_res = 300
          )
}
for (patient in patient_list) {
  print(paste(group_filter, patient))
  if (patient %in% failed) {
    print(" ^    Patient contained no reference group")
    next
  }
  img <- readPNG(paste(paste0(patient, "_CNV"), "/infercnv.png", sep = ""))
  grid::grid.newpage()
  grid::grid.raster(img)
}
## [1] " dummy"

11.1 Dendrogram

Closer look at the dendrogram from the CNV analysis per patient where the nodes as written numbers. Use the numbers to select subgroups for further analysis in ‘t-test on two subgroups’.

#out_dir <- "T1_NANO_012_CNV"
trees <- list()

for (patient in patient_list) {
  if (patient %in% failed) {
    #print("Patient contained no reference group")
    next
  }
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
obv <- read.csv(paste(patient,"_CNV/infercnv.observation_groupings.txt", sep = ""), sep="")
trees[[patient]] <- ggtree(
  tree, ladderize=F) +
  geom_treescale() +
  geom_tiplab(color=obv$Annotation.Color, hjust=-.2) +
  coord_cartesian(clip = 'off') +
  theme_tree2(plot.margin=margin(6, 200, 6, 6)) +
  geom_text2(aes(label=node), hjust=-.3, size = 3) +
  ggplot2::labs(title = patient)
}

grid.arrange(grobs=trees,ncol=3)

t-test chr with groups

Compare the contrast within a chromosome. Select a chromosome, contrast, and patient of interest to run a t-test on.

#########PARAMETERS########
chr <- "chrX" # Select chromosome of interest
contrast <- c("normal", "DKD") # Select contrast
patient <- "dummy"
###########################

positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end")
select_genes <- positions$gene[positions$chr == chr] # Grab chr specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation

filter_counts <- as.data.frame(target_Data@assayData[["log_q"]])
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # filter out samples that are not the interesting region

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # filter out non chromosome specific genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')

for (active_group1 in contrast) {
    for (active_group2 in contrast) {

      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes in", chr, "-", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }

grid.arrange(grobs=plots,ncol=2)

t-test on two subgroups

Select nodes from the dendrogram as subgroups, in this case nodes 18 and 15. Decide on the involved chromosomes and add them into the chr_list. Specify the patient in patient.

#########PARAMETERS########
chr_list <- c("chr22") # Choose needed chromosomes of the target area. Think of it as the x-axis
patient <- "dummy"
node_interest <- 233 # Choose subgroup 1
node_interest2 <- 216 # Choose subgroup 2
###########################

tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
tree_info <- tree %>% as.treedata %>% as_tibble # transform tree into accessible data

samples_interest <- offspring(tree_info, node_interest) # Get labels attached to the group
samples_interest <- as.character(na.omit(samples_interest$label)) # formatting
paste("Subgroup 1: ", samples_interest)
##  [1] "Subgroup 1:  DSP-1001250007864-D-H08"
##  [2] "Subgroup 1:  DSP-1001250007864-D-H10"
##  [3] "Subgroup 1:  DSP-1001250007864-D-H06"
##  [4] "Subgroup 1:  DSP-1001250007864-D-H02"
##  [5] "Subgroup 1:  DSP-1001250007864-D-H04"
##  [6] "Subgroup 1:  DSP-1001250007864-D-H11"
##  [7] "Subgroup 1:  DSP-1001250007864-D-H12"
##  [8] "Subgroup 1:  DSP-1001250007864-D-H05"
##  [9] "Subgroup 1:  DSP-1001250007864-D-H07"
## [10] "Subgroup 1:  DSP-1001250007864-D-H09"
## [11] "Subgroup 1:  DSP-1001250007864-D-H01"
## [12] "Subgroup 1:  DSP-1001250007864-D-H03"
## [13] "Subgroup 1:  DSP-1001250007851-H-A09"
## [14] "Subgroup 1:  DSP-1001250007851-H-C04"
## [15] "Subgroup 1:  DSP-1001250007851-H-D06"
## [16] "Subgroup 1:  DSP-1001250007851-H-C07"
## [17] "Subgroup 1:  DSP-1001250007851-H-D12"
## [18] "Subgroup 1:  DSP-1001250007851-H-A12"
## [19] "Subgroup 1:  DSP-1001250007851-H-A03"
## [20] "Subgroup 1:  DSP-1001250007851-H-D09"
## [21] "Subgroup 1:  DSP-1001250007851-H-A04"
## [22] "Subgroup 1:  DSP-1001250007851-H-D10"
## [23] "Subgroup 1:  DSP-1001250007851-H-A10"
## [24] "Subgroup 1:  DSP-1001250007851-H-C01"
## [25] "Subgroup 1:  DSP-1001250007851-H-C03"
## [26] "Subgroup 1:  DSP-1001250007851-H-A05"
## [27] "Subgroup 1:  DSP-1001250007851-H-C02"
## [28] "Subgroup 1:  DSP-1001250007851-H-B12"
## [29] "Subgroup 1:  DSP-1001250007851-H-B06"
## [30] "Subgroup 1:  DSP-1001250007851-H-B11"
## [31] "Subgroup 1:  DSP-1001250007851-H-B04"
## [32] "Subgroup 1:  DSP-1001250007851-H-B05"
## [33] "Subgroup 1:  DSP-1001250007851-H-D11"
## [34] "Subgroup 1:  DSP-1001250007851-H-D05"
## [35] "Subgroup 1:  DSP-1001250007851-H-B07"
## [36] "Subgroup 1:  DSP-1001250007851-H-C08"
## [37] "Subgroup 1:  DSP-1001250007851-H-D08"
## [38] "Subgroup 1:  DSP-1001250007851-H-B10"
## [39] "Subgroup 1:  DSP-1001250007851-H-A02"
## [40] "Subgroup 1:  DSP-1001250007851-H-A08"
## [41] "Subgroup 1:  DSP-1001250007851-H-A06"
## [42] "Subgroup 1:  DSP-1001250007851-H-A11"
## [43] "Subgroup 1:  DSP-1001250007851-H-D04"
## [44] "Subgroup 1:  DSP-1001250007851-H-D07"
## [45] "Subgroup 1:  DSP-1001250007851-H-B01"
## [46] "Subgroup 1:  DSP-1001250007851-H-B08"
## [47] "Subgroup 1:  DSP-1001250007851-H-A07"
## [48] "Subgroup 1:  DSP-1001250007851-H-B02"
## [49] "Subgroup 1:  DSP-1001250007851-H-B03"
## [50] "Subgroup 1:  DSP-1001250007851-H-C06"
## [51] "Subgroup 1:  DSP-1001250007851-H-D01"
## [52] "Subgroup 1:  DSP-1001250007851-H-C12"
## [53] "Subgroup 1:  DSP-1001250007851-H-C09"
## [54] "Subgroup 1:  DSP-1001250007851-H-C05"
## [55] "Subgroup 1:  DSP-1001250007851-H-C11"
## [56] "Subgroup 1:  DSP-1001250007851-H-C10"
## [57] "Subgroup 1:  DSP-1001250007851-H-B09"
## [58] "Subgroup 1:  DSP-1001250007851-H-D03"
## [59] "Subgroup 1:  DSP-1001250007851-H-D02"
## [60] "Subgroup 1:  DSP-1001250007868-B-A02"
samples_interest2 <- offspring(tree_info, node_interest2)
samples_interest2 <- as.character(na.omit(samples_interest2$label))
paste("Subgroup 2: ", samples_interest2)
##  [1] "Subgroup 2:  DSP-1001250007851-H-F10"
##  [2] "Subgroup 2:  DSP-1001250007851-H-F04"
##  [3] "Subgroup 2:  DSP-1001250007851-H-F11"
##  [4] "Subgroup 2:  DSP-1001250007851-H-F01"
##  [5] "Subgroup 2:  DSP-1001250007851-H-F05"
##  [6] "Subgroup 2:  DSP-1001250007851-H-E08"
##  [7] "Subgroup 2:  DSP-1001250007851-H-F03"
##  [8] "Subgroup 2:  DSP-1001250007851-H-F02"
##  [9] "Subgroup 2:  DSP-1001250007851-H-F09"
## [10] "Subgroup 2:  DSP-1001250007851-H-F06"
## [11] "Subgroup 2:  DSP-1001250007851-H-F07"
## [12] "Subgroup 2:  DSP-1001250007868-B-A05"
## [13] "Subgroup 2:  DSP-1001250007868-B-A03"
## [14] "Subgroup 2:  DSP-1001250007868-B-B01"
## [15] "Subgroup 2:  DSP-1001250007851-H-F12"
## [16] "Subgroup 2:  DSP-1001250007868-B-A04"
## [17] "Subgroup 2:  DSP-1002510007866-C-G02"
## [18] "Subgroup 2:  DSP-1002510007866-C-H12"
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end") # Formatting

select_genes <- positions$gene[positions$chr %in% chr_list] # | positions$chr == chr2] # Grab chromosome specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation[annotation$Sample_ID %in% samples_interest | annotation$Sample_ID %in% samples_interest2,] # Grab subgroup specific Sample IDs

filter_counts <- target_Data@assayData[["log_q"]]
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # Filter Sample ID's

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # Filter genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')


active_group1 <- samples_interest #subgroup 1
active_group2 <- samples_interest2 #subgroup 2

# for (active_group1 in c("sub1")) {
#     for (active_group2 in c("sub2")) {

      #supress reduncant compares
      #if(active_group1==active_group2) {next}
      #comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      #if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      # labels[[counter]]<-paste(active_group1," vs ", active_group2)
      # group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      # group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      #plots[[counter]]<- ggplot(results,
      p <- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes from", chr_list, "in", "subgroup 2"," <- log2(FC) -> Enriched in", "subgroup 1"),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
  #   }
  # }

#ggplotly(p)
p

#grid.arrange(grobs=plots,ncol=2)

12 Code & Versions

Pipelineversion: v1 based on: https://bioconductor.org/packages/devel/workflows/vignettes/GeoMxWorkflows/inst/doc/GeomxTools_RNA-NGS_Analysis.html

The underlying code can be downloaded from the ‘Code’, button on the top of this page. Choose option ‘download Rmd’ to download the full pipeline which can be opened in R or Rstudio. Some filepaths are hardcoded and need to be changed according to your setup.

12.1 R session information

sessionInfo()
## R version 4.2.1 (2022-06-23 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19044)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_Netherlands.utf8  LC_CTYPE=English_Netherlands.utf8   
## [3] LC_MONETARY=English_Netherlands.utf8 LC_NUMERIC=C                        
## [5] LC_TIME=English_Netherlands.utf8    
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] tidytree_0.4.2              ggtree_3.6.2               
##  [3] Biostrings_2.66.0           XVector_0.38.0             
##  [5] ape_5.6-2                   infercnv_1.14.0            
##  [7] Polychrome_1.5.1            preprocessCore_1.60.1      
##  [9] sva_3.35.2                  BiocParallel_1.32.1        
## [11] genefilter_1.80.1           mgcv_1.8-40                
## [13] nlme_3.1-157                scater_1.26.1              
## [15] scuttle_1.8.1               ggalluvial_0.12.3          
## [17] limma_3.54.0                SpatialExperiment_1.8.0    
## [19] SingleCellExperiment_1.20.0 SummarizedExperiment_1.28.0
## [21] GenomicRanges_1.50.1        GenomeInfoDb_1.34.3        
## [23] IRanges_2.32.0              MatrixGenerics_1.10.0      
## [25] matrixStats_0.63.0          standR_1.3.1               
## [27] viridis_0.6.2               viridisLite_0.4.1          
## [29] readxl_1.4.1                png_0.1-7                  
## [31] pathview_1.38.0             fgsea_1.24.0               
## [33] clusterProfiler_4.6.2       forcats_0.5.2              
## [35] stringr_1.4.1               purrr_0.3.5                
## [37] readr_2.1.3                 tidyr_1.2.1                
## [39] tibble_3.1.8                tidyverse_1.3.2            
## [41] gt_0.8.0                    SpatialOmicsOverlay_0.99.11
## [43] RColorBrewer_1.1-3          gridExtra_2.3              
## [45] plotly_4.10.1               DT_0.26                    
## [47] ggrepel_0.9.2               pheatmap_1.0.12            
## [49] Rtsne_0.16                  umap_0.2.9.0               
## [51] cowplot_1.1.1               reshape2_1.4.4             
## [53] scales_1.2.1                ggforce_0.4.1              
## [55] dplyr_1.0.10                knitr_1.41                 
## [57] msigdbr_7.5.1               GSVA_1.46.0                
## [59] SpatialDecon_1.8.0          GeoMxWorkflows_1.5.0       
## [61] GeomxTools_3.2.0            NanoStringNCTools_1.6.0    
## [63] ggplot2_3.4.0               S4Vectors_0.36.0           
## [65] Biobase_2.58.0              BiocGenerics_0.44.0        
## 
## loaded via a namespace (and not attached):
##   [1] graphlayouts_0.8.3        pbapply_1.6-0            
##   [3] lattice_0.20-45           rJava_1.0-6              
##   [5] haven_2.5.1               vctrs_0.5.1              
##   [7] blob_1.2.3                survival_3.3-1           
##   [9] spatstat.data_3.0-0       later_1.3.0              
##  [11] nloptr_2.0.3              DBI_1.1.3                
##  [13] R.utils_2.12.2            rappdirs_0.3.3           
##  [15] uwot_0.1.14               dqrng_0.3.0              
##  [17] jpeg_0.1-9                zlibbioc_1.44.0          
##  [19] mvtnorm_1.1-3             htmlwidgets_1.5.4        
##  [21] future_1.29.0             leiden_0.4.3             
##  [23] logNormReg_0.5-0          parallel_4.2.1           
##  [25] irlba_2.3.5.1             tidygraph_1.2.2          
##  [27] Rcpp_1.0.9                KernSmooth_2.23-20       
##  [29] promises_1.2.0.1          DelayedArray_0.24.0      
##  [31] RcppParallel_5.1.5        magick_2.7.3             
##  [33] graph_1.76.0              RSpectra_0.16-1          
##  [35] fs_1.5.2                  fastmatch_1.1-3          
##  [37] rjags_4-13                digest_0.6.30            
##  [39] sctransform_0.3.5         scatterpie_0.1.8         
##  [41] DOSE_3.24.2               ggraph_2.1.0             
##  [43] pkgconfig_2.0.3           GO.db_3.16.0             
##  [45] spatstat.random_3.0-1     DelayedMatrixStats_1.20.0
##  [47] ggbeeswarm_0.6.0          iterators_1.0.14         
##  [49] minqa_1.2.5               DropletUtils_1.18.0      
##  [51] reticulate_1.26           beeswarm_0.4.0           
##  [53] modeltools_0.2-23         xfun_0.35                
##  [55] bslib_0.4.1               zoo_1.8-11               
##  [57] tidyselect_1.2.0          ica_1.0-3                
##  [59] gson_0.1.0                snow_0.4-4               
##  [61] rlang_1.0.6               jquerylib_0.1.4          
##  [63] glue_1.6.2                EBImage_4.40.0           
##  [65] modelr_0.1.10             lambda.r_1.2.4           
##  [67] labeling_0.4.2            GGally_2.1.2             
##  [69] httpuv_1.6.6              repmis_0.5               
##  [71] TH.data_1.1-1             BiocNeighbors_1.16.0     
##  [73] annotate_1.76.0           jsonlite_1.8.3           
##  [75] bit_4.0.5                 mime_0.12                
##  [77] systemfonts_1.0.4         gplots_3.1.3             
##  [79] BiocStyle_2.26.0          stringi_1.7.8            
##  [81] spatstat.sparse_3.0-0     scattermore_0.8          
##  [83] spatstat.explore_3.0-5    yulab.utils_0.0.6        
##  [85] bitops_1.0-7              cli_3.4.1                
##  [87] rhdf5filters_1.10.0       RSQLite_2.2.18           
##  [89] libcoin_1.0-9             data.table_1.14.6        
##  [91] KEGGgraph_1.58.0          timechange_0.1.1         
##  [93] rstudioapi_0.14           fftwtools_0.9-11         
##  [95] qvalue_2.30.0             fastcluster_1.2.3        
##  [97] locfit_1.5-9.6            listenv_0.8.0            
##  [99] ggthemes_4.2.4            miniUI_0.1.1.1           
## [101] gridGraphics_0.5-1        R.oo_1.25.0              
## [103] dbplyr_2.2.1              lifecycle_1.0.3          
## [105] munsell_0.5.0             cellranger_1.1.0         
## [107] R.methodsS3_1.8.2         caTools_1.18.2           
## [109] codetools_0.2-18          coda_0.19-4              
## [111] vipor_0.4.5               lmtest_0.9-40            
## [113] xtable_1.8-4              ROCR_1.0-11              
## [115] googlesheets4_1.0.1       formatR_1.14             
## [117] BiocManager_1.30.19       scatterplot3d_0.3-42     
## [119] abind_1.4-5               farver_2.1.1             
## [121] parallelly_1.32.1         RANN_2.6.1               
## [123] aplot_0.1.10              askpass_1.1              
## [125] tiff_0.1-11               parallelDist_0.2.6       
## [127] SeuratObject_4.1.3        RcppAnnoy_0.0.20         
## [129] goftest_1.2-3             patchwork_1.1.2          
## [131] futile.options_1.0.1      cluster_2.1.3            
## [133] future.apply_1.10.0       Seurat_4.3.0             
## [135] Matrix_1.5-3              ellipsis_0.3.2           
## [137] lubridate_1.9.0           ggridges_0.5.4           
## [139] googledrive_2.0.0         reprex_2.0.2             
## [141] igraph_1.3.5              lmerTest_3.1-3           
## [143] gargle_1.2.1              argparse_2.2.1           
## [145] spatstat.utils_3.0-1      htmltools_0.5.3          
## [147] BiocFileCache_2.6.0       yaml_2.3.6               
## [149] utf8_1.2.2                XML_3.99-0.12            
## [151] withr_2.5.0               fitdistrplus_1.1-8       
## [153] bit64_4.0.5               multcomp_1.4-20          
## [155] foreach_1.5.2             outliers_0.15            
## [157] progressr_0.11.0          GOSemSim_2.24.0          
## [159] rsvd_1.0.5                ScaledMatrix_1.6.0       
## [161] memoise_2.0.1             evaluate_0.18            
## [163] tzdb_0.3.0                curl_4.3.3               
## [165] fansi_1.0.3               highr_0.9                
## [167] ggiraph_0.8.4             GSEABase_1.60.0          
## [169] tensor_1.5                edgeR_3.40.0             
## [171] cachem_1.0.6              org.Hs.eg.db_3.16.0      
## [173] deldir_1.0-6              HDO.db_0.99.1            
## [175] babelgene_22.9            rjson_0.2.21             
## [177] tools_4.2.1               sandwich_3.0-2           
## [179] sass_0.4.3                magrittr_2.0.3           
## [181] RCurl_1.98-1.9            phyclust_0.1-32          
## [183] ggplotify_0.1.0           xml2_1.3.3               
## [185] httr_1.4.5                assertthat_0.2.1         
## [187] RBioFormats_0.99.9        rmarkdown_2.18           
## [189] boot_1.3-28               globals_0.16.2           
## [191] R6_2.5.1                  Rhdf5lib_1.20.0          
## [193] KEGGREST_1.38.0           treeio_1.22.0            
## [195] gtools_3.9.4              coin_1.4-2               
## [197] beachmat_2.14.0           HDF5Array_1.26.0         
## [199] BiocSingular_1.14.0       rhdf5_2.42.0             
## [201] splines_4.2.1             ggfun_0.0.9              
## [203] colorspace_2.0-3          generics_0.1.3           
## [205] base64enc_0.1-3           gridtext_0.1.5           
## [207] pillar_1.8.1              Rgraphviz_2.42.0         
## [209] tweenr_2.0.2              sp_1.5-1                 
## [211] uuid_1.1-0                R.cache_0.16.0           
## [213] GenomeInfoDbData_1.2.9    plyr_1.8.8               
## [215] gtable_0.3.1              futile.logger_1.4.3      
## [217] rvest_1.0.3               shadowtext_0.1.2         
## [219] fastmap_1.1.0             EnvStats_2.7.0           
## [221] crosstalk_1.2.0           doParallel_1.0.17        
## [223] AnnotationDbi_1.60.0      broom_1.0.1              
## [225] openssl_2.0.4             filelock_1.0.2           
## [227] backports_1.4.1           plotrix_3.8-2            
## [229] lme4_1.1-31               enrichplot_1.18.3        
## [231] hms_1.1.2                 shiny_1.7.3              
## [233] polyclip_1.10-4           grid_4.2.1               
## [235] numDeriv_2016.8-1.1       lazyeval_0.2.2           
## [237] crayon_1.5.2              MASS_7.3-57              
## [239] downloader_0.4            sparseMatrixStats_1.10.0 
## [241] reshape_0.8.9             compiler_4.2.1           
## [243] ggtext_0.1.2              spatstat.geom_3.0-3

12.2 References

knitr::knit_exit()
LS0tDQp0aXRsZTogIk5hbm9zdHJpbmcgR2VvTXggYW5hbHlzaXMiDQphdXRob3I6ICJJZXMgTmlqbWFuICYgUGltIEtsb29zdGVybWFuIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL1VTRVEtbG9nby1zdWJ0aXRsZS5wbmcpDQoNCiMgS2xhbnQ6IFVzZXENCg0KIyBQcm9qZWN0OiBTcGF0aWFsIE9yZ2FuIEF0bGFzDQoNCiMgRGF0YXNldDogS2lkbmV5DQoNCioqZGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICclSCAlTSAlYSAlZCAlQiwgJVknKWAqKg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCioqbG9hZGluZyBkZXBlbmRlbmNpZXMqKiBQbGVhc2UgbWFrZSBzdXJlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlDQppbnN0YWxsZWQgYW5kIHJlcXVpcmVkIGxpYnJhcmllcyBjYW4gYmUgbG9hZGVkOg0KDQotICAgaW5zdGFsbC5wYWNrYWdlcygicGtnYnVpbGQiKSAvLyBwa2didWlsZDo6Y2hlY2tfYnVpbGRfdG9vbHMoKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCi0gICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIk5hbm9zdHJpbmctQmlvc3RhdHMvTmFub1N0cmluZ05DVG9vbHMiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiTmFub3N0cmluZy1CaW9zdGF0cy9HZW9teFRvb2xzIiwgcmVmID0NCiAgICAiZGV2IikNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR2VvTXhXb3JrZmxvd3MiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlNwYXRpYWxEZWNvbiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdTVkEiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygiRFQiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIm1zaWdkYnIiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRpZ2VzdCIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZSIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkVCSW1hZ2UiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ3NjYXR0ZXJtb3JlJykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwYmFwcGx5JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwbG90cml4JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdnZ3RleHQnKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ1JCaW9Gb3JtYXRzJykNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiYW9sZXMvUkJpb0Zvcm1hdHMiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIkM6L1VzZXJzL2luaWptYW4vRG93bmxvYWRzL1NwYXRpYWxPbWljc092ZXJsYXktMC45OS4xMy1iZXRhLnRhci5neiIsDQogICAgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBOVUxMKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoImNsdXN0ZXJQcm9maWxlciIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhdGh2aWV3IikNCg0KYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNsb2FkIGxpYnJhcmllcw0KDQojc3RhY2tfc2l6ZSA8LSBnZXRPcHRpb24oInBhbmRvYy5zdGFjay5zaXplIiwgZGVmYXVsdCA9ICI1MTJtIikNCiNvcHRpb25zKGphdmEucGFyYW1ldGVycyA9IGMoIi1YWDorVXNlQ29uY01hcmtTd2VlcEdDIiwgIi1YbXg4MTkybSwgLVhYOk1ldGFzcGFjZVNpemU9MTAyNE0iKSkNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gYygiLVhYOitVc2VDb25jTWFya1N3ZWVwR0MiLCAiLVhteDgxOTJtIikpDQpsaWJyYXJ5KE5hbm9TdHJpbmdOQ1Rvb2xzKQ0KbGlicmFyeShHZW9teFRvb2xzKQ0KbGlicmFyeShHZW9NeFdvcmtmbG93cykNCmxpYnJhcnkoU3BhdGlhbERlY29uKQ0KbGlicmFyeShHU1ZBKSAjZm9yIHBhdGh3YXkgYW5hbHlzZXMNCmxpYnJhcnkobXNpZ2RicikgI2ZvciBtb2xlY3VsYXIgc2lnbmF0dXJlcyBpbiBwYXRod2F5IGFuYWx5c2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dmb3JjZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhbGVzKSAjIGZvciBwZXJjZW50DQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBmb3IgbWVsdA0KbGlicmFyeShjb3dwbG90KSAgICMgZm9yIHBsb3RfZ3JpZA0KbGlicmFyeSh1bWFwKQ0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkocGhlYXRtYXApICAjIGZvciBwaGVhdG1hcA0KbGlicmFyeShnZ3JlcGVsKSANCmxpYnJhcnkoc2NhbGVzKSAjZm9yIGdncGxvdCBwZWF1ZG9sb2cgdG8gcHJldmVudCBlcnJvcnMgb24gbG9nKDApDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShTcGF0aWFsT21pY3NPdmVybGF5KQ0KbGlicmFyeShndCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KGZnc2VhKQ0KbGlicmFyeShwYXRodmlldykNCmxpYnJhcnkocG5nKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmxpYnJhcnkoc3RhbmRSKQ0KbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkNCmxpYnJhcnkobGltbWEpDQpsaWJyYXJ5KGdnYWxsdXZpYWwpDQpsaWJyYXJ5KHNjYXRlcikNCg0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJzdmEiKQ0KbGlicmFyeShzdmEpDQoNCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgicHJlcHJvY2Vzc0NvcmUiKSAjcXVhbnRpbGUgbm9ybQ0KbGlicmFyeShwcmVwcm9jZXNzQ29yZSkNCg0KI2luc3RhbGwucGFja2FnZXMoIlBvbHljaHJvbWUiKSAjR2V0IGNvbG9ycw0KbGlicmFyeShQb2x5Y2hyb21lKQ0KDQoNCmxpYnJhcnkoaW5mZXJjbnYpICMgQ05WDQpsaWJyYXJ5KCJhcGUiKQ0KbGlicmFyeSgiQmlvc3RyaW5ncyIpDQpsaWJyYXJ5KCJnZ3RyZWUiKQ0KbGlicmFyeSh0aWR5dHJlZSkNCmBgYA0KDQojIDEgbG9hZGluZyBiYXNlIGZpbGVzDQoNCmBgYHtyIGxvYWRpbmdfYmFzZV9kYXRhfQ0KIyBSZWZlcmVuY2UgdGhlIG1haW4gZm9sZGVyICdmaWxlLnBhdGgnIGNvbnRhaW5pbmcgdGhlIHN1Yi1mb2xkZXJzIHdpdGggZWFjaCBkYXRhIGZpbGUgdHlwZToNCmRhdGFkaXI8LWZpbGUucGF0aCgiTDovcGtsb29zdGVybWFuL0dpdGh1Yi9ES0RfS2lkbmV5LyIpDQpgYGANCg0KVG8gbG9jYXRlIGEgc3BlY2lmaWMgZmlsZSBwYXRoIHJlcGxhY2UgdGhlIGFib3ZlIGxpbmUgd2l0aCBkYXRhZGlyIFw8LQ0KZmlsZS5wYXRoKCJcfi9Gb2xkZXIvU3ViRm9sZGVyL0RhdGFMb2NhdGlvbiIpIHJlcGxhY2UgdGhlIEZvbGRlciwNClN1YkZvbGRlciwgRGF0YUxvY2F0aW9uIGFzIG5lZWRlZC4gVGhlIERhdGFMb2NhdGlvbiBmb2xkZXIgc2hvdWxkDQpjb250YWluIGEgZGNjcywgcGtjcywgYW5kIGFubm90YXRpb24gZm9sZGVyIHdpdGggZWFjaCBzZXQgb2YgZmlsZXMNCnByZXNlbnQgYXMgbmVlZGVkIGF1dG9tYXRpY2FsbHkgbGlzdCBmaWxlcyBpbiBlYWNoIGRpcmVjdG9yeSBmb3IgdXNlLg0KDQoqKlRha2UgY2FyZSB5b3UgaW1wb3J0IGEgY29sdW1uIHdpdGggbnVjbGVpIGNvdW50IHNlcGFyYXRlbHkgaWYgeW91DQp3YW50LioqDQoNCmBgYHtyIHBhcnNlX2ZpbGVzfQ0KRENDRmlsZXMgPC0gZGlyKGZpbGUucGF0aChkYXRhZGlyLCAiZGNjcyIpLCBwYXR0ZXJuID0gIi5kY2MkIiwNCiAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNClBLQ0ZpbGVzIDwtIGRpcihmaWxlLnBhdGgoZGF0YWRpciwgInBrY3MiKSwgcGF0dGVybiA9ICIucGtjJCIsDQogICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpTYW1wbGVBbm5vdGF0aW9uRmlsZSA8LQ0KICBkaXIoZmlsZS5wYXRoKGRhdGFkaXIsICJhbm5vdGF0aW9uIiksIHBhdHRlcm4gPSAiXltefl0iLA0KICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpgYGANCg0KIyAyIGxvYWQgZGF0YQ0KDQpgYGB7ciBsb2FkX2RhdGF9DQpEYXRhIDwtDQogIHJlYWROYW5vU3RyaW5nR2VvTXhTZXQoZGNjRmlsZXMgPSBEQ0NGaWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBwa2NGaWxlcyA9IFBLQ0ZpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFTaGVldCA9ICJUZW1wbGF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRGNjQ29sTmFtZSA9ICJTYW1wbGVfSUQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YUNvbE5hbWVzID0gYygiYW9pIiwgInJvaSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KDQojc2F2ZSBkYXRhIHRvIHByZXZlbnQgbG9hZGluZyB0aW1lIGZvciByZXRha2VzDQojc2F2ZURhdGE8LURhdGENCiNEYXRhPC1zYXZlRGF0YQ0KDQojY2hhbmdlIERhdGEgY29sdW1uIG5hbWVzIGFuZCBtYW51YWwgY29ycmVjdGlvbiBvZiBzcGVsbGluZyBlcnJvcnMNCkRhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZV9uYW1lIl1dPC1EYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGUgbmFtZSJdXQ0KRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlIG5hbWUiXV08LSAgTlVMTA0KDQoNCiMrMSByZWZlcmVuY2VzIHRoZSBzbGlkZSBuYW1lIGNvbHVtbg0KYW5uX3NpemU8LWxlbmd0aChjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKVtncmVwbCgiQU5OIixjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKSldKSsxIA0KYW5uX25hbWVzPC1jKGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpW2dyZXBsKCJBTk4iLGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpKV0sInNsaWRlX25hbWUiKQ0KDQojIEZlZWwgZnJlZSB0byBjaGFuZ2UgdGhlIG9yZGVyIG9mIHdoaWNoIGNvbG9ycyBhcmUgYXBwb2ludGVkLg0KY29sb3I8LWMoIiNBMzQ5QTQiLCAiI0ZGRkYzMyIsICIjRTcyOThBIiwgIiMwOTE4MzMiLCAiIzFCOUU3NyIsICIjRDk1RjAyIiwgIiM3NTcwQjMiLCAgIiM2NkE2MUUiLCAiI0U2QUIwMiIsICIjOEREM0M3IiwgIiM5RjAwMEYiLCAiI0JFQkFEQSIsICIjRkI4MDcyIiwgIiM4MEIxRDMiLCAiI0ZEQjQ2MiIsICIjQjNERTY5IiwgIiNGQ0NERTUiLCAiI0Q5RDlEOSIsICIjQkM4MEJEIiwgIiNDQ0VCQzUiLCAiI0ZGRUQ2RiIsICIjMzc3RUI4IiwgIiM5ODRFQTMiLCAiIzREQUY0QSIsICIjRkY3MUNFIiwgIiNGRjdGMDAiLCAiI0E2Q0VFMyIsICIjMUY3OEI0IiwgIiNCMkRGOEEiLCAiIzMzQTAyQyIsICIjRkI5QTk5IiwgIiNFMzFBMUMiLCAiI0ZEQkY2RiIsICIjQ0FCMkQ2IiwgIiM2QTNEOUEiLCAiI0ZGRkY5OSIsICIjQjE1OTI4IikNCg0KIyBVc2UgY291bnQgYW5kIGNvdW50X21heCB0byBzZXQgdGhlIHJhbmdlIG9mIGNvbG9yIG5lZWRlZCBmb3IgZWFjaCBjb2x1bW4uDQojIFdpdGggc2V0TmFtZXMoKSB0aGUgY29sb3IgY29kZSBpcyBsaW5rZWQgdG8gZWFjaCB1bmlxdWUgaW5kaXZpZHVhbCB2YWx1ZSBvZiBlYWNoIGNvbHVtbi4NCmNvdW50PTENCmNvbG9yX2xpc3QgPSBsaXN0KCkNCmZvciAoYW5uIGluIGFubl9uYW1lcykgew0KICBjb3VudF9tYXggPSBjb3VudCtsZW5ndGgodW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSktMQ0KICBjb2xvcl9saXN0W1thbm5dXTwtc2V0TmFtZXMoY29sb3JbY291bnQ6Y291bnRfbWF4XSwgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCiAgY291bnQ9Y291bnRfbWF4KzENCn0NCmBgYA0KDQpgYGB7ciBjb2xvciB0YWJsZX0NCnZhbHVlX25hbWVzID0gYygpDQpmb3IgKGFubiBpbiBhbm5fbmFtZXMpIHsNCiAgdmFsdWVfbmFtZXMgPC0gYyh2YWx1ZV9uYW1lcywgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCn0NCg0KY29sb3JfZGYgPC0gYXMuZGF0YS5mcmFtZSh2YWx1ZV9uYW1lcykNCmNvbG9yX2RmJGNvbG9yIDwtIGNvbG9yWzA6bGVuZ3RoKHZhbHVlX25hbWVzKV0NCg0KY29sb3JfZGYgJT4lIA0KICBtdXRhdGUoY29sb3IgPSBmY3RfaW5vcmRlcihjb2xvcikpIHw+IA0KICBndCgpICU+JSANCiAgZGF0YV9jb2xvcihjb2x1bW5zID0gY29sb3IsIGNvbG9ycyA9IGFzLmNoYXJhY3Rlcihjb2xvcikpICU+JQ0KICB0YWJfb3B0aW9ucyhjb250YWluZXIuaGVpZ2h0ID0gNTAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGFzdGUoIlJlYWRzIGZyb20gZm9sbG93aW5nIHJ1bnMgdXNlZDogIix1bmlxdWUocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSRTZXFTZXRJZCkpDQpgYGANCg0KIyAzIFN0dWR5IGRlc2lnbg0KDQpgYGB7ciBhbm5vdGF0ZX0NCnBrY3MgPC0gYW5ub3RhdGlvbihEYXRhKQ0KbW9kdWxlcyA8LSBnc3ViKCIucGtjIiwgIiIsIHBrY3MpDQprYWJsZShkYXRhLmZyYW1lKFBLQ3MgPSBwa2NzLCBtb2R1bGVzID0gbW9kdWxlcykpDQpgYGANCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9ucyB3ZSB3YW50IHRvIHNob3csIHVzZSBcYFxgIHRvIHN1cnJvdW5kIGNvbHVtbg0KbmFtZXMgd2l0aCBzcGFjZXMgb3Igc3BlY2lhbCBzeW1ib2xzDQoNCmBgYHtyIHNlbGVjdF9hbm5vdGF0aW9uc30NCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KHBEYXRhKERhdGEpLCBBTk4xLEFOTjIsc2xpZGVfbmFtZSkNCmNvdW50X21hdCA8LSBwRGF0YShEYXRhKSAlPiUgZHBseXI6OmNvdW50KHBEYXRhKERhdGEpW2MoYW5uX25hbWVzKV0pDQpgYGANCg0KU2ltcGxpZnkgdGhlIHNsaWRlIG5hbWVzIGlmIHJlcXVpcmVkDQoNCmBgYHtyIHNpbXBsaWZ5X25hbWVzfQ0KIyBjb3VudF9tYXQkc2xpZGVfbmFtZSA8LSBnc3ViKCJkaXNlYXNlIiwgImQiLCBnc3ViKCJub3JtYWwiLCAibiIsIGNvdW50X21hdCRzbGlkZV9uYW1lKSkNCiNjb3VudF9tYXQkcGF0aF9hbm4gPC0gZ3N1YigiaSIsICIiLCBjb3VudF9tYXQkcGF0aF9hbm4pICNjb3JyZWN0aW5nIHNwZWxsaW5nIGVycm9yDQpgYGANCg0KR2F0aGVyIHRoZSBkYXRhIGFuZCBwbG90IGluIG9yZGVyOiBjbGFzcywgc2xpZGUgbmFtZSwgcmVnaW9uLCBzZWdtZW50DQoNCmBgYHtyIGdhdGhlcl9kYXRhfQ0KdGVzdF9nciA8LSBnYXRoZXJfc2V0X2RhdGEoY291bnRfbWF0LCAxOmFsbF9vZihhbm5fc2l6ZSkpDQp0ZXN0X2dyJHggPC0gZmFjdG9yKHRlc3RfZ3IkeCwgbGFiZWxzID0gYW5uX25hbWVzKQ0KYGBgDQoNClBsb3QgU2Fua2V5DQoNCmBgYHtyIFNhbmtleV9wbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMX0NCmdncGxvdCh0ZXN0X2dyLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCwgYWVzKHgsIGlkID0gaWQsIHNwbGl0ID0geSwgdmFsdWUgPSBuKSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHMoYWVzKGZpbGwgPSBBTk4yKSwgYWxwaGEgPSAwLjUsIGF4aXMud2lkdGggPSAwLjEpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2F4ZXMoYXhpcy53aWR0aCA9IDAuMikgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfbGFiZWxzKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsNCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDQuMjUsIHhlbmQgPSA0LjI1LA0KICAgICAgICAgICB5ID0gMTAsIHllbmQgPSA2MSwgbHdkID0gMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNC4xOSwgeSA9IDI1LCBhbmdsZSA9IDkwLCBzaXplID0gNSwNCiAgICAgICAgICAgaGp1c3QgPSAwLjUsIGxhYmVsID0gIjUwIHNlZ21lbnRzIikNCmBgYA0KDQojIDQgUUMgJiBQcmUtcHJvY2Vzc2luZw0KDQpTaGlmdCBjb3VudHMgdG8gb25lDQoNCmBgYHtyIHNoaWZ0X2NvdW50c30NCiNzaGlmdCBhbnkgZXhwcmVzc2lvbiBjb3VudHMgd2l0aCBhIHZhbHVlIG9mIDAgdG8gMSB0byBlbmFibGUgaW4gZG93bnN0cmVhbSB0cmFuc2Zvcm1hdGlvbnMuDQpEYXRhIDwtIHNoaWZ0Q291bnRzT25lKERhdGEsIHVzZURBTG9naWMgPSBUUlVFKQ0KYGBgDQoNCiMgNC4xIFNlZ21lbnQgUUMNCg0KV2UgZmlyc3QgYXNzZXNzIHNlcXVlbmNpbmcgcXVhbGl0eSBhbmQgYWRlcXVhdGUgdGlzc3VlIHNhbXBsaW5nIGZvcg0KZXZlcnkgUk9JL0FPSSBzZWdtZW50Lg0KDQpFdmVyeSBST0kvQU9JIHNlZ21lbnQgd2lsbCBiZSB0ZXN0ZWQgZm9yOg0KDQpSYXcgc2VxdWVuY2luZyByZWFkczogc2VnbWVudHMgd2l0aCBcPjEwMDAgcmF3IHJlYWRzIGFyZSByZW1vdmVkLiAlDQpBbGlnbmVkLCUgVHJpbW1lZCwgb3IgJSBTdGl0Y2hlZCBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyBiZWxvdyBcfjgwJQ0KZm9yIG9uZSBvciBtb3JlIG9mIHRoZXNlIFFDIHBhcmFtZXRlcnMgYXJlIHJlbW92ZWQuICUgU2VxdWVuY2luZw0Kc2F0dXJhdGlvbiAoWzEtZGVkdXBsaWNhdGVkIHJlYWRzL2FsaWduZWQgcmVhZHNdJSk6IHNlZ21lbnRzIGJlbG93IFx+NTAlDQpyZXF1aXJlIGFkZGl0aW9uYWwgc2VxdWVuY2luZyB0byBjYXB0dXJlIGZ1bGwgc2FtcGxlIGRpdmVyc2l0eSBhbmQgYXJlDQpub3QgdHlwaWNhbGx5IGFuYWx5emVkIHVudGlsIGltcHJvdmVkLiBOZWdhdGl2ZSBDb3VudDogdGhpcyBpcyB0aGUNCmdlb21ldHJpYyBtZWFuIG9mIHRoZSBzZXZlcmFsIHVuaXF1ZSBuZWdhdGl2ZSBwcm9iZXMgaW4gdGhlIEdlb014IHBhbmVsDQp0aGF0IGRvIG5vdCB0YXJnZXQgbVJOQSBhbmQgZXN0YWJsaXNoIHRoZSBiYWNrZ3JvdW5kIGNvdW50IGxldmVsIHBlcg0Kc2VnbWVudDsgc2VnbWVudHMgd2l0aCBsb3cgbmVnYXRpdmUgY291bnRzICgxLTEwKSBhcmUgbm90IG5lY2Vzc2FyaWx5DQpyZW1vdmVkIGJ1dCBtYXkgYmUgc3R1ZGllZCBjbG9zZXIgZm9yIGxvdyBlbmRvZ2Vub3VzIGdlbmUgc2lnbmFsIGFuZC9vcg0KaW5zdWZmaWNpZW50IHRpc3N1ZSBzYW1wbGluZy4gTm8gVGVtcGxhdGUgQ29udHJvbCAoTlRDKSBjb3VudDogdmFsdWVzDQpcPjEsMDAwIGNvdWxkIGluZGljYXRlIGNvbnRhbWluYXRpb24gZm9yIHRoZSBzZWdtZW50cyBhc3NvY2lhdGVkIHdpdGgNCnRoaXMgTlRDOyBob3dldmVyLCBpbiBjYXNlcyB3aGVyZSB0aGUgTlRDIGNvdW50IGlzIGJldHdlZW4gMSwwMDAtDQoxMCwwMDAsIHRoZSBzZWdtZW50cyBtYXkgYmUgdXNlZCBpZiB0aGUgTlRDIGRhdGEgaXMgdW5pZm9ybWx5IGxvdyAoZS5nLg0KMC0yIGNvdW50cyBmb3IgYWxsIHByb2JlcykuIE51Y2xlaTogXD4xMDAgbnVjbGVpIHBlciBzZWdtZW50IGlzDQpnZW5lcmFsbHkgcmVjb21tZW5kZWQ7IGhvd2V2ZXIsIHRoaXMgY3V0b2ZmIGlzIGhpZ2hseSBzdHVkeS90aXNzdWUNCmRlcGVuZGVudCBhbmQgbWF5IG5lZWQgdG8gYmUgcmVkdWNlZDsgd2hhdCBpcyBtb3N0IGltcG9ydGFudCBpcw0KY29uc2lzdGVuY3kgaW4gdGhlIG51Y2xlaSBkaXN0cmlidXRpb24gZm9yIHNlZ21lbnRzIHdpdGhpbiB0aGUgc3R1ZHkuDQpBcmVhOiBnZW5lcmFsbHkgY29ycmVsYXRlcyB3aXRoIG51Y2xlaTsgYSBzdHJpY3QgY3V0b2ZmIGlzIG5vdCBnZW5lcmFsbHkNCmFwcGxpZWQgYmFzZWQgb24gYXJlYS4NCg0KIyA0LjEuMSBTZWxlY3QgU2VnbWVudCBRQw0KDQpGaXJzdCwgd2Ugc2VsZWN0IHRoZSBRQyBwYXJhbWV0ZXIgY3V0b2ZmcywgYWdhaW5zdCB3aGljaCBvdXIgUk9JL0FPSQ0Kc2VnbWVudHMgd2lsbCBiZSB0ZXN0ZWQgYW5kIGZsYWdnZWQgYXBwcm9wcmlhdGVseS4gV2UgaGF2ZSBzZWxlY3RlZCB0aGUNCmFwcHJvcHJpYXRlIHN0dWR5LXNwZWNpZmljIHBhcmFtZXRlcnMgZm9yIHRoaXMgc3R1ZHkuIE5vdGU6IHRoZSBkZWZhdWx0DQpRQyB2YWx1ZXMgcmVjb21tZW5kZWQgYWJvdmUgYXJlIGFkdmlzZWQgd2hlbiBzdXJ2ZXlpbmcgYSBuZXcgZGF0YXNldCBmb3INCnRoZSBmaXJzdCB0aW1lLg0KDQpEZWZhdWx0IFFDIGN1dG9mZnMgYXJlIGNvbW1lbnRlZCBpbiAoKSBhZGphY2VudCB0byB0aGUgcmVzcGVjdGl2ZQ0KcGFyYW1ldGVycyBzdHVkeS1zcGVjaWZpYyB2YWx1ZXMgd2VyZSBzZWxlY3RlZCBhZnRlciB2aXN1YWxpemluZyB0aGUgUUMNCnJlc3VsdHMgaW4gbW9yZSBkZXRhaWwgYmVsb3cNCg0KYGBge3Igc2V0X1FDX3BhcmFtc30NClFDX3BhcmFtcyA8LQ0KICBsaXN0KG1pblNlZ21lbnRSZWFkcyA9IDEwMDAsICMgTWluaW11bSBudW1iZXIgb2YgcmVhZHMgKDEwMDApDQogICAgICAgcGVyY2VudFRyaW1tZWQgPSA4MCwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgdHJpbW1lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTdGl0Y2hlZCA9IDgwLCAgICMgTWluaW11bSAlIG9mIHJlYWRzIHN0aXRjaGVkICg4MCUpDQogICAgICAgcGVyY2VudEFsaWduZWQgPSA3NSwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgYWxpZ25lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTYXR1cmF0aW9uID0gNTAsICMgTWluaW11bSBzZXF1ZW5jaW5nIHNhdHVyYXRpb24gKDUwJSkNCiAgICAgICBtaW5OZWdhdGl2ZUNvdW50ID0gMSwgICAjIE1pbmltdW0gbmVnYXRpdmUgY29udHJvbCBjb3VudHMgKDEwKQ0KICAgICAgIG1heE5UQ0NvdW50ID0gOTAwMCwgICAgICMgTWF4aW11bSBjb3VudHMgb2JzZXJ2ZWQgaW4gTlRDIHdlbGwgKDEwMDApDQogICAgICAgbWluTnVjbGVpID0gMjAsICAgICAgICAjIE1pbmltdW0gIyBvZiBudWNsZWkgZXN0aW1hdGVkICgxMDApDQogICAgICAgbWluQXJlYSA9IDEwMDApICAgICAgICAgIyBNaW5pbXVtIHNlZ21lbnQgYXJlYSAoNTAwMCkNCkRhdGEgPC0NCiAgc2V0U2VnbWVudFFDRmxhZ3MoRGF0YSwgcWNDdXRvZmZzID0gUUNfcGFyYW1zKSAgICAgICAgDQoNCmNhdCgicHJlLVFDIGZlYXR1cmVzOiIsIGRpbShEYXRhKVsxXSwgIlxucHJlLVFDIHNhbXBsZXM6IiwgZGltKERhdGEpWzJdKQ0KDQojVGFibGUgZm9yIGNsYXJpZmljYXRpb24NClFDcGFyYW1zX2RmIDwtIGRhdGEuZnJhbWUgKA0KICBpdGVtcyA9IGMoIm1pblNlZ21lbnRSZWFkcyIsInBlcmNlbnRUcmltbWVkIiwicGVyY2VudFN0aXRjaGVkIiwicGVyY2VudEFsaWduZWQiLCJwZXJjZW50U2F0dXJhdGlvbiIsDQogICAgICAgICAgICAibWluTmVnYXRpdmVDb3VudCIsIm1heE5UQ0NvdW50IiwibWluTnVjbGVpIiwibWluQXJlYSIpLA0KICBkZWZhdWx0cyA9IGMoMTAwMCw4MCw4MCw4MCw1MCwxMCwxMDAwLDEwMCw1MDAwKSwNCiAgYWN0dWFsID0gYyhRQ19wYXJhbXMkbWluU2VnbWVudFJlYWRzLFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCxRQ19wYXJhbXMkcGVyY2VudFN0aXRjaGVkLFFDX3BhcmFtcyRwZXJjZW50QWxpZ25lZCxRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24sUUNfcGFyYW1zJG1pbk5lZ2F0aXZlQ291bnQsUUNfcGFyYW1zJG1heE5UQ0NvdW50LFFDX3BhcmFtcyRtaW5OdWNsZWksUUNfcGFyYW1zJG1pbkFyZWEpDQopDQoNCmRhdGF0YWJsZShRQ3BhcmFtc19kZiwgcm93bmFtZXM9RkFMU0UsDQogICAgICAgICAgY2FwdGlvbiA9ICJRQyB0aHJlc2hvbGRzIiwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICkNCikNCg0KYGBgDQoNCkNvbGxhdGUgUUMgUmVzdWx0cw0KDQpgYGB7ciBjb2xsYXRlX1FDX3Jlc3VsdHN9DQpRQ1Jlc3VsdHMgPC0gcHJvdG9jb2xEYXRhKERhdGEpW1siUUNGbGFncyJdXQ0KZmxhZ19jb2x1bW5zIDwtIGNvbG5hbWVzKFFDUmVzdWx0cykNClFDX1N1bW1hcnkgPC0gZGF0YS5mcmFtZShQYXNzID0gY29sU3VtcyghUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgV2FybmluZyA9IGNvbFN1bXMoUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSkpDQoNClFDUmVzdWx0cyRRQ1N0YXR1cyA8LSBhcHBseShRQ1Jlc3VsdHMsIDFMLCBmdW5jdGlvbih4KSB7DQogIGlmZWxzZShzdW0oeCkgPT0gMEwsICJQQVNTIiwgIldBUk5JTkciKQ0KfSkNCg0KUUNfU3VtbWFyeVsiVE9UQUwgRkxBR1MiLCBdIDwtDQogIGMoc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJQQVNTIiksDQogICAgc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJXQVJOSU5HIikpDQoNCmNvbF9ieSA8LSAiQU5OMSINCmNvbF9ieV9wbGF0ZSA8LSAiUGxhdGVfSUQiDQpgYGANCg0KIyA0LjIgR3JhcGhpY2FsIHN1bW1hcmllcyBvZiBRQyBzdGF0aXN0aWNzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCmBgYHtyIFFDX3Bsb3R0aW5nfQ0KUUNfaGlzdG9ncmFtIDwtIGZ1bmN0aW9uKGFzc2F5X2RhdGEgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb24gPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxfYnkgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRociA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfdHJhbnMgPSBOVUxMKSB7DQogIHBsdCA8LSBnZ3Bsb3QoYXNzYXlfZGF0YSwNCiAgICAgICAgICAgICAgICBhZXNfc3RyaW5nKHggPSBwYXN0ZTAoInVubGlzdChgIiwgYW5ub3RhdGlvbiwgImApIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmlsbF9ieSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSB0aHIsIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgICB0aGVtZV9idygpICsgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsNCiAgICBmYWNldF93cmFwKGFzLmZvcm11bGEocGFzdGUoIn4iLCBmaWxsX2J5KSksIG5yb3cgPSA0KSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMSkgKw0KICAgIGxhYnMoeCA9IGFubm90YXRpb24sIHkgPSAic2VnbWVudHMsICMiLCB0aXRsZSA9IGFubm90YXRpb24pDQogIGlmKCFpcy5udWxsKHNjYWxlX3RyYW5zKSkgew0KICAgIHBsdCA8LSBwbHQgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gc2NhbGVfdHJhbnMpDQogIH0NCiAgcGx0DQp9DQpgYGANCg0KIyMgVHJpbW1lZA0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIlRyaW1tZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFRyaW1tZWQpDQpgYGANCg0KIyMgU3RpY2hlZCAoJSkNCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJTdGl0Y2hlZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50U3RpdGNoZWQpDQpgYGANCg0KIyMgQWxpZ25lZCAoJSkNCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJBbGlnbmVkICglKSIsIGNvbF9ieSxRQ19wYXJhbXMkcGVyY2VudEFsaWduZWQpDQpgYGANCg0KIyMgU2VxdWVuY2luZyBTYXR1cmF0aW9uICglKSB7LmFjdGl2ZX0NCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJTYXR1cmF0ZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pICsNCiAgbGFicyh0aXRsZSA9ICJTZXF1ZW5jaW5nIFNhdHVyYXRpb24gKCUpIiwNCiAgICAgICB4ID0gIlNlcXVlbmNpbmcgU2F0dXJhdGlvbiAoJSkiKQ0KYGBgDQoNCiMjIEFyZWENCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJhcmVhIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluQXJlYSwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KYGBgDQoNCiMjIE51Y2xlaSBjb3VudA0KDQpgYGB7cn0NClFDX2hpc3RvZ3JhbShzRGF0YShEYXRhKSwgIm51Y2xlaSIsIGNvbF9ieSwgUUNfcGFyYW1zJG1pbk51Y2xlaSkNCmBgYA0KDQojIyBEdXBsaWNhdGlvblJhdGUNCg0KYGBge3J9DQpnZ3Bsb3QocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSwNCiAgICAgICBhZXMoeCA9IFBsYXRlX0lELCBmaWxsPVBsYXRlX0lELA0KICAgICAgICAgIHkgPSAoRGVkdXBsaWNhdGVkUmVhZHMvUmF3KSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCAvIFJhdyByZWFkcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMgTmVncHJvYmVzIHZzIEVuZG9nZW5vdXMNCg0KYGBge3IgcGxvdF9uZWdwcm9iZV9kYXRhLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQ0KdG1wX3RhcmdldF9EYXRhIDwtIGFnZ3JlZ2F0ZUNvdW50cyhEYXRhKQ0KDQojZ2V0IG5lZ2F0aXZlIHByb2JlIGRhdGENCm5lZ3M8LXN1YnNldCh0bXBfdGFyZ2V0X0RhdGEsQ29kZUNsYXNzPT0iTmVnYXRpdmUiKQ0KDQpwMTwtZ2dwbG90KHBEYXRhKG5lZ3MpLA0KICAgICAgIGFlcyh4ID0gQU5OMiwgZmlsbCA9IEFOTjIsDQogICAgICAgICAgeSA9IGFzc2F5RGF0YUVsZW1lbnQobmVncywgZWx0ID0gImV4cHJzIikpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJOZWdhdGl2ZSBwcm9iZXMgRXhwcmVzc2lvbiIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwzMDAwKSwgdHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKQ0KDQoNCiMgZ2V0IGVuZG9nZW5vdXMgcHJvYmUgZGF0YQ0KZW5kPC1zdWJzZXQodG1wX3RhcmdldF9EYXRhLENvZGVDbGFzcz09IkVuZG9nZW5vdXMiKQ0KDQpwMjwtZ2dwbG90KHBEYXRhKGVuZCksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICAgeSA9IGNvbE1lYW5zKGFzc2F5RGF0YUVsZW1lbnQoZW5kLCBlbHQgPSAiZXhwcnMiKSkpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJFbmRvZ2Vub3VzIHByb2JlcyBFeHByZXNzaW9uIChtZWFuKSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwzMDAwKSx0cmFucyA9ICJsb2cyIikgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCnBsIDwtbGlzdChwMSxwMikNCnBsb3RfZ3JpZChwbG90bGlzdD1wbCwgbnJvdz0yLCBhbGlnbj0ndicpDQoNCmBgYA0KDQojIyBOZWdfcHJvYmUgcmVhZHMgY29tcGFyZWQgdG8gcmF3X3JlYWRzDQoNCmBgYHtyfQ0KDQojIG1ha2UgYmFja2dyb3VuZCB0b3RhbCBuZWcgcHJvYmUgY291bnQNCmZkYXRhX2RmPC1mRGF0YShEYXRhKQ0KbmVncHJvYmVzbmFtZXM8LXJvd25hbWVzKGZkYXRhX2RmW2ZkYXRhX2RmJE5lZ2F0aXZlPT1UUlVFLF0pDQp0ZW1wX2V4cDwtYXNzYXlEYXRhRWxlbWVudChEYXRhLGVsdD0nZXhwcnMnKQ0KbmVncHJvYmVfZXhwcl9mZDwtdGVtcF9leHBbcm93bmFtZXModGVtcF9leHApICVpbiUgbmVncHJvYmVzbmFtZXMsXQ0KdG90X25lZ19jdHJsX3JlYWRzPC1jb2xTdW1zKG5lZ3Byb2JlX2V4cHJfZmQpDQp0b3RfZGVkdXBfcmVhZHM8LXBEYXRhKHByb3RvY29sRGF0YShEYXRhKSkkRGVkdXBsaWNhdGVkUmVhZHMNCg0KZGY8LWRhdGEuZnJhbWUoJ2FvaSc9IG5hbWVzKHRvdF9uZWdfY3RybF9yZWFkcyksJ3RvdF9kZWR1cF9yZWFkcycgPSBhcy5udW1lcmljKHRvdF9kZWR1cF9yZWFkcyksJ3RvdF9uZWdfY3RybF9yZWFkcyc9YXMubnVtZXJpYyh0b3RfbmVnX2N0cmxfcmVhZHMpKQ0KZGY8LW1lbHQoZGYsaWQ9ImFvaSIpDQpnZ3Bsb3QoZGYsYWVzKGZpbGw9dmFyaWFibGUseT12YWx1ZSx4PWFvaSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uPSJpZGVudGl0eSIsc3RhdD0iaWRlbnRpdHkiKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZzJfdHJhbnMoKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSkgICAgICAgICAgICAgICAgICAgICANCiANCmBgYA0KDQojIyBEdXBsaWNhdGVkIHJlYWRzIHZzIEJhY2tncm91bmQNCg0KYGBge3J9DQojIGdldCBkY2MgcGVyIHBsYXRlLiBzdW0gbmVncHJvYmUgY291bnRzL2RjYy9wbGF0ZQ0KZ2dwbG90KHBEYXRhKHByb3RvY29sRGF0YShEYXRhKSksDQogICAgICAgYWVzKHggPSBQbGF0ZV9JRCwgZmlsbD1QbGF0ZV9JRCwNCiAgICAgICAgICB5ID0gRGVkdXBsaWNhdGVkUmVhZHMpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJEZWR1cGxpY2F0ZWQgLyBSYXcgcmVhZHMiKSArDQogIHNjYWxlX3lfbG9nMTAoKSsNCiAgZ2VvbV9obGluZShkYXRhID1wRGF0YShwcm90b2NvbERhdGEoRGF0YSkpICwgDQogICAgICAgICAgIGFlcyh5aW50ZXJjZXB0ID0gTlRDLCBjb2xvdXI9UGxhdGVfSUQpKSArDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgUk9JYXJlYQ0KDQpgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9NX0NCnRlbXBfZGY8LWNiaW5kKHBEYXRhKERhdGEpLHBEYXRhKHByb3RvY29sRGF0YShEYXRhKSksZGNjPXJvd25hbWVzKHBEYXRhKERhdGEpKSkNCg0KZ2dwbG90KHRlbXBfZGYsDQogICAgICAgYWVzKHggPSBkY2MsIGNvbG91cj1zbGlkZV9uYW1lLA0KICAgICAgICAgIHkgPSAoRGVkdXBsaWNhdGVkUmVhZHMvYXJlYSkgKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB5bGltKDAsMjApICsgDQogIGxhYnMoeSA9ICJEZWR1cGxpY2F0ZWQgcmVhZHMgLyBST0kgYXJlYSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9NiwgYW5nbGU9OTAsIGhqdXN0PTEpICkNCg0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgbnVjbGVpDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KdGVtcF9kZjwtY2JpbmQocERhdGEoRGF0YSkscERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSxkY2M9cm93bmFtZXMocERhdGEoRGF0YSkpKQ0KDQpnZ3Bsb3QodGVtcF9kZiwNCiAgICAgICBhZXMoeCA9IGRjYywgY29sb3VyPXNsaWRlX25hbWUsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9udWNsZWkpICkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxpbSgwLDUwKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGp1c3QgcGVyIHByb2plY3QNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCByZWFkcyAvIG51Y2xlaSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9NiwgYW5nbGU9OTAsIGhqdXN0PTEpICkNCg0KYGBgDQoNCiMgNC4zIFByb2Nlc3MgTmVnYXRpdmUgR2VvTWVhbnMNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgbmVnYXRpdmUgZ2VvbWV0cmljIG1lYW5zIGZvciBlYWNoIG1vZHVsZQ0KIyBJdCB3aWxsIHNob3cgb25seSB0aGUgbmVnYXRpdmUgcHJvYmVzIGdlb21lYW4sIHNvIGV4cGVjdCBsZXNzIHNlZ21lbnRzLg0KbmVnYXRpdmVHZW9NZWFucyA8LSANCiAgZXNCeShuZWdhdGl2ZUNvbnRyb2xTdWJzZXQoRGF0YSksIA0KICAgICAgIEdST1VQID0gIk1vZHVsZSIsIA0KICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsgDQogICAgICAgICBhc3NheURhdGFBcHBseSh4LCBNQVJHSU4gPSAyLCBGVU4gPSBuZ2VvTWVhbiwgZWx0ID0gImV4cHJzIikgDQogICAgICAgfSkgDQpwcm90b2NvbERhdGEoRGF0YSlbWyJOZWdHZW9NZWFuIl1dIDwtIG5lZ2F0aXZlR2VvTWVhbnMNCg0KbmVnQ29scyA8LSBwYXN0ZTAoIk5lZ0dlb01lYW5fIiwgbW9kdWxlcykNCnBEYXRhKERhdGEpWywgbmVnQ29sc10gPC0gc0RhdGEoRGF0YSlbWyJOZWdHZW9NZWFuIl1dDQpmb3IoYW5uIGluIG5lZ0NvbHMpIHsNCiAgcGx0IDwtIFFDX2hpc3RvZ3JhbShwRGF0YShEYXRhKSwgYW5uLCBjb2xfYnksIDIsIHNjYWxlX3RyYW5zID0gImxvZzEwIikNCiAgcHJpbnQocGx0KQ0KfQ0KDQoNCiMgRGV0YXRjaCBuZWdfZ2VvbWVhbiBjb2x1bW5zIGFoZWFkIG9mIGFnZ3JlZ2F0ZUNvdW50cyBjYWxsDQoNCnBEYXRhKERhdGEpIDwtIHBEYXRhKERhdGEpWywgIWNvbG5hbWVzKHBEYXRhKERhdGEpKSAlaW4lIG5lZ0NvbHNdDQoNCmBgYA0KDQpTaG93IGFsbCBOVEMgdmFsdWVzLCBGcmVxID0gXCMgb2YgU2VnbWVudHMgd2l0aCBhIGdpdmVuIE5UQyBjb3VudDoNCg0KYGBge3IgUUNfdGFibGVzfQ0KUUM8LXNEYXRhKERhdGEpDQoNCm50Y19kZiA8LVFDWyxjKCJzbGlkZV9uYW1lIiwiUGxhdGVfSUQiLCJOVENfSUQiLCJOVEMiKV0NCnRlbXB0YWJsZTwtbnRjX2RmICU+JSBkcGx5cjo6Y291bnQobnRjX2RmJHNsaWRlX25hbWUsbnRjX2RmJE5UQ19JRCxudGNfZGYkUGxhdGVfSUQsbnRjX2RmJE5UQykNCmNvbG5hbWVzKHRlbXB0YWJsZSkgPC0gYygiU2xpZGVfbmFtZSIsIk5UQ19JRCIsIlBsYXRlX0lEIiwiTlRDX2NvdW50IiwiTnVtYmVyX29mX3NhbXBsZXMiKQ0KZGF0YXRhYmxlKHRlbXB0YWJsZSwgcm93bmFtZXMgPSBGQUxTRSkNCg0KDQprYWJsZSh0YWJsZShOVENfQ291bnQgPSBzRGF0YShEYXRhKSROVEMpLCBjb2wubmFtZXMgPSBjKCJOVEMgQ291bnQiLCAiIyBvZiBTZWdtZW50cyIpKQ0KDQprYWJsZShRQ19TdW1tYXJ5LCBjYXB0aW9uID0gIlFDIFN1bW1hcnkgVGFibGUgZm9yIGVhY2ggU2VnbWVudCIpDQoNCmRhdGF0YWJsZShRQ19TdW1tYXJ5LA0KICAgICAgICAgIGNhcHRpb24gPSAiQU9JIFFDIFN1bW1hcnkiLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKQ0KYGBgDQoNClNob3cgQU9JcyB3aGljaCBmYWlsIGNyaXRpY2FsIFFDcy4NCg0KYGBge3IgbGlzdF9mYWlsdXJlc30NClFDPC1zRGF0YShEYXRhKQ0KdW5kZXJzYXQ8LXN1YnNldChRQywgYFNhdHVyYXRlZCAoJSlgPD0gUUNfcGFyYW1zJHBlcmNlbnRTYXR1cmF0aW9uKQ0KDQppZihucm93KHVuZGVyc2F0KT4gMCkgew0KDQpkYXRhdGFibGUoYWdncmVnYXRlKHVuZGVyc2F0LCBieT1saXN0KHVuZGVyc2F0JFNhbXBsZUlEKSxwYXN0ZSxjb2xsYXBzZT0iOyIpLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKQ0KKX0NCmBgYA0KDQpTdWJzZXR0aW5nIG91ciBkYXRhc2V0IGhhcyByZW1vdmVkIHNhbXBsZXMgd2hpY2ggZGlkIG5vdCBwYXNzIFFDDQoNCmBgYHtyIHN1YnNldHRpbmdfUUNfZmFpbHN9DQpEYXRhIDwtIERhdGFbLCBRQ1Jlc3VsdHMkUUNTdGF0dXMgPT0gIlBBU1MiXQ0KYGBgDQoNCkdlbmVyYWxseSBrZWVwIHRoZSBxY0N1dG9mZnMgcGFyYW1ldGVycyB1bmNoYW5nZWQuIFNldA0KcmVtb3ZlTG9jYWxPdXRsaWVycyB0byBGQUxTRSBpZiB5b3UgZG8gbm90IHdhbnQgdG8gcmVtb3ZlIGxvY2FsIG91dGxpZXJzDQoNCmBgYHtyIHByb2Nlc3NfUUN9DQpEYXRhIDwtIHNldEJpb1Byb2JlUUNGbGFncyhEYXRhLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxY0N1dG9mZnMgPSBsaXN0KG1pblByb2JlUmF0aW8gPSAwLjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjZW50RmFpbEdydWJicyA9IDIwKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlTG9jYWxPdXRsaWVycyA9IEZBTFNFKQ0KDQpQcm9iZVFDUmVzdWx0cyA8LSBmRGF0YShEYXRhKVtbIlFDRmxhZ3MiXV0NCmBgYA0KDQpEZWZpbmUgUUMgdGFibGUgZm9yIFByb2JlIFFDDQoNCmBgYHtyIGRlZmluZV9xY190YWJsZX0NCnFjX2RmIDwtIGRhdGEuZnJhbWUoUGFzc2VkID0gc3VtKHJvd1N1bXMoUHJvYmVRQ1Jlc3VsdHNbLCAtMV0pID09IDApLA0KICAgICAgICAgICAgICAgICAgICBHbG9iYWwgPSBzdW0oUHJvYmVRQ1Jlc3VsdHMkR2xvYmFsR3J1YmJzT3V0bGllciksDQogICAgICAgICAgICAgICAgICAgIExvY2FsID0gc3VtKHJvd1N1bXMoUHJvYmVRQ1Jlc3VsdHNbLCAtMjotMV0pID4gMA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmICFQcm9iZVFDUmVzdWx0cyRHbG9iYWxHcnViYnNPdXRsaWVyKSkNCmBgYA0KDQpTdWJzZXQgb2JqZWN0IHRvIGV4Y2x1ZGUgYWxsIHRoYXQgZGlkIG5vdCBwYXNzIFJhdGlvICYgR2xvYmFsIHRlc3RpbmcNCg0KYGBge3Igc3Vic2V0fQ0KUHJvYmVRQ1Bhc3NlZCA8LSANCiAgc3Vic2V0KERhdGEsIA0KICAgICAgICAgZkRhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dWyxjKCJMb3dQcm9iZVJhdGlvIildID09IEZBTFNFICYNCiAgICAgICAgICAgZkRhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dWyxjKCJHbG9iYWxHcnViYnNPdXRsaWVyIildID09IEZBTFNFKQ0KDQpEYXRhIDwtIFByb2JlUUNQYXNzZWQgDQpjYXQoIkFmdGVyIFFDIGZlYXR1cmVzOiIsIGRpbShEYXRhKVsxXSwgIlxuQWZ0ZXIgUUMgc2FtcGxlczoiLCBkaW0oRGF0YSlbMl0pDQpgYGANCg0KQ2hlY2sgaG93IG1hbnkgdW5pcXVlIHRhcmdldHMgdGhlIG9iamVjdCBoYXMNCg0KYGBge3IgdW5pcXVlX2NoZWNrfQ0KbGVuZ3RoKHVuaXF1ZShmZWF0dXJlRGF0YShEYXRhKVtbIlRhcmdldE5hbWUiXV0pKQ0KYGBgDQoNCkNvbGxhcHNlIHRvIHRhcmdldHMNCg0KYGBge3IgY29sbGFwc190YXJnZXRzfQ0KdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQoNCmV4cHJzKHRhcmdldF9EYXRhKVsxOjUsIDE6Ml0NCmBgYA0KDQpEZWZpbmUgTE9RIFNEIHRocmVzaG9sZCBhbmQgbWluaW11bSB2YWx1ZQ0KDQpgYGB7ciBzZXRfTFNRfQ0KY3V0b2ZmIDwtIDINCm1pbkxPUSA8LSAyDQpgYGANCg0KIyA0LjQgTGltaXQgb2YgUXVhbnRpZmljYXRpb24NCg0KV2UgZGVmaW5lIGEgbGltaXQgb2YgcXVhbnRpZmljYXRpb24gKExPUSkgcGVyIFJPSS9BT0kgc2VnbWVudCBiYXNlZCBvbg0KdGhlIG5lZ2F0aXZlIGNvbnRyb2wgcHJvYmVzIHRvIGd1aWRlIHRoZSBmaWx0ZXJpbmcgb2Ygc2VnbWVudHMgYW5kIGdlbmVzDQp3aXRoIGxvdyBzaWduYWwgcmVsYXRpdmUgdG8gYmFja2dyb3VuZC4gVGhlIGZvcm11bGEgZm9yIGNhbGN1bGF0aW5nIHRoZQ0KTE9RIGluIHRoZSAkaV57dGh9JCBzZWdtZW50IGF0ICRuJCBzdGFuZGFyZCBkZXZpYXRpb25zICgkbiA9IDIkIGZvciB0aGlzDQpzdHVkeSkgaXM6ICRMT1FfaT1nZW9tZWFuKE5lZ1Byb2JlX2kpKmdlb1NEKE5lZ1Byb2JlX2kpXm4kDQoNCkNhbGN1bGF0ZSBMT1EgcGVyIG1vZHVsZSB0ZXN0ZWQNCg0KYGBge3IgY2FsY3VsYXRlX0xPUX0NCkxPUSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKHRhcmdldF9EYXRhKSkNCmZvcihtb2R1bGUgaW4gbW9kdWxlcykgew0KICB2YXJzIDwtIHBhc3RlMChjKCJOZWdHZW9NZWFuXyIsICJOZWdHZW9TRF8iKSwNCiAgICAgICAgICAgICAgICAgbW9kdWxlKQ0KICBpZihhbGwodmFyc1sxOjJdICVpbiUgY29sbmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSkpIHsNCiAgICBMT1FbLCBtb2R1bGVdIDwtDQogICAgICBwbWF4KG1pbkxPUSwNCiAgICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpWywgdmFyc1sxXV0gKiANCiAgICAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCB2YXJzWzJdXSBeIGN1dG9mZikNCiAgfQ0KfQ0KcERhdGEodGFyZ2V0X0RhdGEpJExPUSA8LSBMT1ENCmBgYA0KDQojIDQuNSBGaWx0ZXJpbmcNCg0KQWZ0ZXIgZGV0ZXJtaW5pbmcgdGhlIGxpbWl0IG9mIHF1YW50aWZpY2F0aW9uIChMT1EpIHBlciBzZWdtZW50LCB3ZQ0KcmVjb21tZW5kIGZpbHRlcmluZyBvdXQgZWl0aGVyIHNlZ21lbnRzIGFuZC9vciBnZW5lcyB3aXRoIGFibm9ybWFsbHkgbG93DQpzaWduYWwuIEZpbHRlcmluZyBpcyBhbiBpbXBvcnRhbnQgc3RlcCB0byBmb2N1cyBvbiB0aGUgdHJ1ZSBiaW9sb2dpY2FsDQpkYXRhIG9mIGludGVyZXN0Lg0KDQpXZSBkZXRlcm1pbmUgdGhlIG51bWJlciBvZiBnZW5lcyBkZXRlY3RlZCBpbiBlYWNoIHNlZ21lbnQgYWNyb3NzIHRoZQ0KZGF0YXNldC4NCg0KYGBge3IgZmlsdGVyaW5nfQ0KTE9RX01hdCA8LSBjKCkNCmZvcihtb2R1bGUgaW4gbW9kdWxlcykgew0KICBpbmQgPC0gZkRhdGEodGFyZ2V0X0RhdGEpJE1vZHVsZSA9PSBtb2R1bGUNCiAgTWF0X2kgPC0gdChlc0FwcGx5KHRhcmdldF9EYXRhW2luZCwgXSwgTUFSR0lOID0gMSwNCiAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICAgICAgICAgeCA+IExPUVssIG1vZHVsZV0NCiAgICAgICAgICAgICAgICAgICAgIH0pKQ0KICBMT1FfTWF0IDwtIHJiaW5kKExPUV9NYXQsIE1hdF9pKQ0KfQ0KIyBlbnN1cmUgb3JkZXJpbmcgc2luY2UgdGhpcyBpcyBzdG9yZWQgb3V0c2lkZSBvZiB0aGUgZ2VvbXhTZXQNCkxPUV9NYXQgPC0gTE9RX01hdFtmRGF0YSh0YXJnZXRfRGF0YSkkVGFyZ2V0TmFtZSwgXQ0KYGBgDQoNCiMgNC41LjEgU2VnbWVudCBHZW5lIERldGVjdGlvbg0KDQpXZSBmaXJzdCBmaWx0ZXIgb3V0IHNlZ21lbnRzIHdpdGggZXhjZXB0aW9uYWxseSBsb3cgc2lnbmFsLiBUaGVzZQ0Kc2VnbWVudHMgd2lsbCBoYXZlIGEgc21hbGwgZnJhY3Rpb24gb2YgcGFuZWwgZ2VuZXMgZGV0ZWN0ZWQgYWJvdmUgdGhlDQpMT1EgcmVsYXRpdmUgdG8gdGhlIG90aGVyIHNlZ21lbnRzIGluIHRoZSBzdHVkeS4gTGV0J3MgdmlzdWFsaXplIHRoZQ0KZGlzdHJpYnV0aW9uIG9mIHNlZ21lbnRzIHdpdGggcmVzcGVjdCB0byB0aGVpciAlIGdlbmVzIGRldGVjdGVkOg0KDQpTYXZlIGRldGVjdGlvbiByYXRlIGluZm9ybWF0aW9uIHRvIHBoZW5vIGRhdGENCg0KYGBge3Igc2F2ZV9kZXRlY3Rpbm9fcmF0ZX0NCnBEYXRhKHRhcmdldF9EYXRhKSRHZW5lc0RldGVjdGVkIDwtIA0KICBjb2xTdW1zKExPUV9NYXQsIG5hLnJtID0gVFJVRSkNCnBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSA8LQ0KICBwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZXNEZXRlY3RlZCAvIG5yb3codGFyZ2V0X0RhdGEpDQoNCmBgYA0KDQpEZXRlcm1pbmUgZGV0ZWN0aW9uIHRocmVzaG9sZHM6IDElLCA1JSwgMTAlLCAxNSUsIFw+MTUlDQoNCmBgYHtyIGRldGVybWluZSt0aHJlc2hvbGRzfQ0KcERhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblRocmVzaG9sZCA8LSANCiAgY3V0KHBEYXRhKHRhcmdldF9EYXRhKSRHZW5lRGV0ZWN0aW9uUmF0ZSwNCiAgICAgIGJyZWFrcyA9IGMoMCwgMC4wMSwgMC4wNSwgMC4xLCAwLjE1LCAwLjIsMSksDQogICAgICBsYWJlbHMgPSBjKCI8MSUiLCAiMS01JSIsICI1LTEwJSIsICIxMC0xNSUiLCAiMTUtMjAlIiwgIj4yMCUiKSkNCg0KIyBzdGFja2VkIGJhciBwbG90IG9mIGRpZmZlcmVudCBjdXQgcG9pbnRzICgxJSwgNSUsIDEwJSwgMTUlKQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IERldGVjdGlvblRocmVzaG9sZCkpICsNCiAgZ2VvbV9iYXIoYWVzKGZpbGwgPSBBTk4yKSkgKw0KICBnZW9tX3RleHQoc3RhdCA9ICJjb3VudCIsIGFlcyhsYWJlbCA9IC4uY291bnQuLiksIHZqdXN0ID0gLTAuNSkgKw0KICB0aGVtZV9idygpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjEpKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4yKSArDQogIGxhYnMoeCA9ICJHZW5lIERldGVjdGlvbiBSYXRlIiwNCiAgICAgICB5ID0gIlNlZ21lbnRzLCAjIiwNCiAgICAgICBmaWxsID0gIlNlZ21lbnQgVHlwZSIpDQpgYGANCg0KY3V0IHBlcmNlbnQgZ2VuZXMgZGV0ZWN0ZWQgYXQgMSwgNSwgMTAsIDE1DQoNCmBgYHtyIGN1dF90b19wZXJjZW50fQ0Ka2FibGUodGFibGUocERhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblRocmVzaG9sZCwNCiAgICAgICAgICAgIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4yKSkNCg0KIyBzZXQgdGhyZXNob2xkIGZvciBkZXRlY3Rpb25sZXZlbA0KIyBkZWZhdWx0IDAuMQ0KZ2VuZV9kZXRfdGhyZXNob2xkIDwtIDAuMDUNCg0KdGFyZ2V0X0RhdGEgPC0NCiAgdGFyZ2V0X0RhdGFbLCBwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZURldGVjdGlvblJhdGUgPj0gZ2VuZV9kZXRfdGhyZXNob2xkXQ0KDQpkaW0odGFyZ2V0X0RhdGEpDQpgYGANCg0KIyA0LjYgTWFudWFsIHJlbW92YWwgb2Ygc2FtcGxlcy9jbGFzc2VzDQoNCmBgYHtyIHJlbW92ZV9zYW1wbGVzfQ0KYWN0aXZlX2FvaXM8LW5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpDQpgYGANCg0KcmUtQ29sbGVjdCBhbm5vdGF0aW9ucw0KDQpgYGB7ciBjb2xsZWN0X2Fubm90YXRpb25zfQ0KIyBnYXRoZXIgdGhlIGRhdGEgYW5kIHBsb3QgaW4gb3JkZXI6IGNsYXNzLCBzbGlkZSBuYW1lLCByZWdpb24sIHNlZ21lbnQNCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KHBEYXRhKERhdGEpLCBBTk4xLEFOTjIsQU5OMyxBTk40LHNsaWRlX25hbWUpDQoNCnRlbXBfcWMgPC0gdGVtcF9kZg0KdGVtcF9xYyRRQ1Jlc3VsdCA8LSBRQ1Jlc3VsdHMkUUNTdGF0dXMNCnRlbXBfcWMkUUNSZXN1bHRbdGVtcF9xYyRRQ1Jlc3VsdCA9PSAiV0FSTklORyJdIDwtICJYIg0KDQojY291bnRfbWF0IDwtIGRwbHlyOjpjb3VudChhLCBBTk4xLCBBTk4yLCBzbGlkZV9uYW1lLCBRQ1Jlc3VsdCkNCmNvdW50X21hdCA8LSB0ZW1wX3FjICU+JSBkcGx5cjo6Y291bnQodGVtcF9xY1tjKGFubl9uYW1lcywgIlFDUmVzdWx0IildKQ0KDQp0ZXN0X2dyIDwtIGdhdGhlcl9zZXRfZGF0YShjb3VudF9tYXQsIDE6KGFubl9zaXplKzEpKQ0KdGVzdF9nciR4IDwtIGZhY3Rvcih0ZXN0X2dyJHgsIGxhYmVscyA9IGMoYW5uX25hbWVzLCAiUUNSZXN1bHQiKSkNCmBgYA0KDQpyZS1QbG90IFNhbmtleQ0KDQpgYGB7ciBwbG90X3NhbmtleSwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTF9DQpnZ3Bsb3QodGVzdF9nciwgYWVzKHgsIGlkID0gaWQsIHNwbGl0ID0geSwgdmFsdWUgPSBuKSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHMoYWVzKGZpbGwgPSBBTk4yKSwgYWxwaGEgPSAwLjUsIGF4aXMud2lkdGggPSAwLjEpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2F4ZXMoYXhpcy53aWR0aCA9IDAuMikgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfbGFiZWxzKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsNCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNykgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sb3JfbGlzdCRBTk4yKSArDQogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDMuMjUsIHhlbmQgPSAzLjI1LCB5ID0gMTAsIA0KICAgICAgICAgICB5ZW5kID0gNjAsIGx3ZCA9IDIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDMuMTksIHkgPSAyNSwgYW5nbGUgPSA5MCwgc2l6ZSA9IDUsDQogICAgICAgICAgIGhqdXN0ID0gMC41LCBsYWJlbCA9ICI1MCBzZWdtZW50cyIpDQpgYGANCg0KIyA0LjcgR2VuZSBEZXRlY3Rpb24gUmF0ZQ0KDQpDYWxjdWxhdGUgZGV0ZWN0aW9uIHJhdGUNCg0KYGBge3IgY2xjX2RldGVjdGlvbl9yYXRlfQ0KTE9RX01hdCA8LSBMT1FfTWF0WywgY29sbmFtZXModGFyZ2V0X0RhdGEpXQ0KZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGVkU2VnbWVudHMgPC0gcm93U3VtcyhMT1FfTWF0LCBuYS5ybSA9IFRSVUUpDQpmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA8LQ0KICBmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0ZWRTZWdtZW50cyAvIG5yb3cocERhdGEodGFyZ2V0X0RhdGEpKQ0KYGBgDQoNCkdlbmUgb2YgaW50ZXJlc3QgZGV0ZWN0aW9uIHRhYmxlDQoNCmBgYHtyIGdlbmVfb2ZfaW50ZXJlc3RfdGFibGV9DQpnb2kgPC0gYygiUERDRDEiLCAiQ0QyNzQiLCAiSUZORyIsICJDRDhBIiwgIkNENjgiLCAiRVBDQU0iLA0KICAgICAgICAgIktSVDE4IiwgIk5QSFMxIiwgIk5QSFMyIiwgIkNBTEIxIiwgIkNMRE44IikNCmdvaV9kZiA8LSBkYXRhLmZyYW1lKA0KICBHZW5lID0gZ29pLA0KICBOdW1iZXIgPSBmRGF0YSh0YXJnZXRfRGF0YSlbZ29pLCAiRGV0ZWN0ZWRTZWdtZW50cyJdLA0KICBEZXRlY3Rpb25SYXRlID0gcGVyY2VudChmRGF0YSh0YXJnZXRfRGF0YSlbZ29pLCAiRGV0ZWN0aW9uUmF0ZSJdKSkNCmBgYA0KDQojIDQuOCBHZW5lIEZpbHRlcmluZw0KDQpXZSB3aWxsIGdyYXBoIHRoZSB0b3RhbCBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgaW4gZGlmZmVyZW50DQpwZXJjZW50YWdlcyBvZiBzZWdtZW50cy4gQmFzZWQgb24gdGhlIHZpc3VhbGl6YXRpb24gYmVsb3csIHdlIGNhbiBiZXR0ZXINCnVuZGVyc3RhbmQgZ2xvYmFsIGdlbmUgZGV0ZWN0aW9uIGluIG91ciBzdHVkeSBhbmQgc2VsZWN0IGhvdyBtYW55IGxvdw0KZGV0ZWN0ZWQgZ2VuZXMgdG8gZmlsdGVyIG91dCBvZiB0aGUgZGF0YXNldC4gR2VuZSBmaWx0ZXJpbmcgaW5jcmVhc2VzDQpwZXJmb3JtYW5jZSBvZiBkb3duc3RyZWFtIHN0YXRpc3RpY2FsIHRlc3RzIGFuZCBpbXByb3ZlcyBpbnRlcnByZXRhdGlvbg0Kb2YgdHJ1ZSBiaW9sb2dpY2FsIHNpZ25hbC4NCg0KUGxvdCBkZXRlY3Rpb24gcmF0ZQ0KDQpgYGB7ciBwbG90X2RldF9yYXRlfQ0KcGxvdF9kZXRlY3QgPC0gZGF0YS5mcmFtZShGcmVxID0gYygxLCA1LCAxMCwgMjAsIDMwLCA1MCkpDQpwbG90X2RldGVjdCROdW1iZXIgPC0NCiAgdW5saXN0KGxhcHBseShjKDAuMDEsIDAuMDUsIDAuMSwgMC4yLCAwLjMsIDAuNSksDQogICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkge3N1bShmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA+PSB4KX0pKQ0KcGxvdF9kZXRlY3QkUmF0ZSA8LSBwbG90X2RldGVjdCROdW1iZXIgLyBucm93KGZEYXRhKHRhcmdldF9EYXRhKSkNCnJvd25hbWVzKHBsb3RfZGV0ZWN0KSA8LSBwbG90X2RldGVjdCRGcmVxDQoNCmdncGxvdChwbG90X2RldGVjdCwgYWVzKHggPSBhcy5mYWN0b3IoRnJlcSksIHkgPSBSYXRlLCBmaWxsID0gUmF0ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdEMoTnVtYmVyLCBmb3JtYXQgPSAiZCIsIGJpZy5tYXJrID0gIiwiKSksDQogICAgICAgICAgICB2anVzdCA9IDEuNiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gNCkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAib3JhbmdlMiIsIG1pZCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gImRvZGdlcmJsdWUzIiwgbWlkcG9pbnQgPSAwLjY1LA0KICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsMSksDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9idygpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwgbGltaXRzID0gYygwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDApKSkgKw0KICBsYWJzKHggPSAiJSBvZiBTZWdtZW50cyIsDQogICAgICAgeSA9ICJHZW5lcyBEZXRlY3RlZCwgJSBvZiBQYW5lbCA+IExPUSIpDQpgYGANCg0KU3Vic2V0IHRvIHRhcmdldCBnZW5lcyBkZXRlY3RlZCBpbiBhdCBsZWFzdCAxMCUgb2YgdGhlIHNhbXBsZXMuIEFsc28NCm1hbnVhbGx5IGluY2x1ZGUgdGhlIG5lZ2F0aXZlIGNvbnRyb2wgcHJvYmUsIGZvciBkb3duc3RyZWFtIHVzZQ0KDQpgYGB7ciBzdWJzZXRfdG9fMTBwX2RldGVjdGVkX2dlbmVzfQ0KIyBkZWZhdWx0PTAuMQ0KbmVnYXRpdmVQcm9iZWZEYXRhIDwtIHN1YnNldChmRGF0YSh0YXJnZXRfRGF0YSksIENvZGVDbGFzcyA9PSAiTmVnYXRpdmUiKQ0KbmVnX3Byb2JlcyA8LSB1bmlxdWUobmVnYXRpdmVQcm9iZWZEYXRhJFRhcmdldE5hbWUpDQp0YXJnZXRfRGF0YSA8LSANCiAgdGFyZ2V0X0RhdGFbZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblJhdGUgPj0gMC4wNSB8DQogICAgICAgICAgICAgICAgICAgIGZEYXRhKHRhcmdldF9EYXRhKSRUYXJnZXROYW1lICVpbiUgbmVnX3Byb2JlcywgXQ0KDQojIHJldGFpbiBvbmx5IGRldGVjdGVkIGdlbmVzIG9mIGludGVyZXN0DQpnb2kgPC0gZ29pW2dvaSAlaW4lIHJvd25hbWVzKHRhcmdldF9EYXRhKV0NCg0KZGltKHRhcmdldF9EYXRhKQ0KYGBgDQoNCiMgNSBOb3JtYWxpemF0aW9uDQoNCldlIHdpbGwgbm93IG5vcm1hbGl6ZSB0aGUgR2VvTXggZGF0YSBmb3IgZG93bnN0cmVhbSB2aXN1YWxpemF0aW9ucyBhbmQNCmRpZmZlcmVudGlhbCBleHByZXNzaW9uLiBUaGUgdHdvIGNvbW1vbiBtZXRob2RzIGZvciBub3JtYWxpemF0aW9uIG9mDQpEU1AtTkdTIFJOQSBkYXRhIGFyZSBpKSBxdWFydGlsZSAzIChRMykgb3IgaWkpIGJhY2tncm91bmQgbm9ybWFsaXphdGlvbi4NCg0KQm90aCBvZiB0aGVzZSBub3JtYWxpemF0aW9uIG1ldGhvZHMgZXN0aW1hdGUgYSBub3JtYWxpemF0aW9uIGZhY3RvciBwZXINCnNlZ21lbnQgdG8gYnJpbmcgdGhlIHNlZ21lbnQgZGF0YSBkaXN0cmlidXRpb25zIHRvZ2V0aGVyLiBNb3JlIGFkdmFuY2VkDQptZXRob2RzIGZvciBub3JtYWxpemF0aW9uIGFuZCBtb2RlbGluZyBhcmUgdW5kZXIgYWN0aXZlIGRldmVsb3BtZW50Lg0KSG93ZXZlciwgZm9yIG1vc3Qgc3R1ZGllcywgdGhlc2UgbWV0aG9kcyBhcmUgc3VmZmljaWVudCBmb3INCnVuZGVyc3RhbmRpbmcgZGlmZmVyZW5jZXMgYmV0d2VlbiBiaW9sb2dpY2FsIGNsYXNzZXMgb2Ygc2VnbWVudHMgYW5kDQpzYW1wbGVzLg0KDQpRMyBub3JtYWxpemF0aW9uIGlzIHR5cGljYWxseSB0aGUgcHJlZmVycmVkIG5vcm1hbGl6YXRpb24gc3RyYXRlZ3kgZm9yDQptb3N0IERTUC1OR1MgUk5BIHN0dWRpZXMuIEdpdmVuIHRoZSBsb3cgbmVnYXRpdmUgcHJvYmUgY291bnRzIGluIHRoaXMNCnBhcnRpY3VsYXIgZGF0YXNldCBhcyBzaG93biBkdXJpbmcgU2VnbWVudCBRQywgd2Ugd291bGQgZnVydGhlciBhdm9pZA0KYmFja2dyb3VuZCBub3JtYWxpemF0aW9uIGFzIGl0IG1heSBiZSBsZXNzIHN0YWJsZS4NCg0KQmVmb3JlIG5vcm1hbGl6YXRpb24sIHdlIHdpbGwgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHVwcGVyDQpxdWFydGlsZSAoUTMpIG9mIHRoZSBjb3VudHMgaW4gZWFjaCBzZWdtZW50IHdpdGggdGhlIGdlb21ldHJpYyBtZWFuIG9mDQp0aGUgbmVnYXRpdmUgY29udHJvbCBwcm9iZXMgaW4gdGhlIGRhdGEuIElkZWFsbHksIHRoZXJlIHNob3VsZCBiZSBhDQpzZXBhcmF0aW9uIGJldHdlZW4gdGhlc2UgdHdvIHZhbHVlcyB0byBlbnN1cmUgd2UgaGF2ZSBzdGFibGUgbWVhc3VyZSBvZg0KUTMgc2lnbmFsLiBJZiB5b3UgZG8gbm90IHNlZSBzdWZmaWNpZW50IHNlcGFyYXRpb24gYmV0d2VlbiB0aGVzZSB2YWx1ZXMsDQp5b3UgbWF5IGNvbnNpZGVyIG1vcmUgYWdncmVzc2l2ZSBmaWx0ZXJpbmcgb2YgbG93IHNpZ25hbCBzZWdtZW50cy9nZW5lcy4NCg0KR3JhcGggUTMgdmFsdWUgdnMgbmVnR2VvTWVhbiBvZiBOZWdhdGl2ZXMNCg0KYGBge3IgbHBvdF9xM19uZWdHZW9NZWFufQ0KYW5uX29mX2ludGVyZXN0IDwtICJBTk4yIg0KU3RhdF9kYXRhIDwtIA0KICBkYXRhLmZyYW1lKHJvdy5uYW1lcyA9IGNvbG5hbWVzKGV4cHJzKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgU2VnbWVudCA9IGNvbG5hbWVzKGV4cHJzKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgQW5ub3RhdGlvbiA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9vZl9pbnRlcmVzdF0sDQogICAgICAgICAgICAgUTMgPSB1bmxpc3QoYXBwbHkoZXhwcnModGFyZ2V0X0RhdGEpLCAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YW50aWxlLCAwLjc1LCBuYS5ybSA9IFRSVUUpKSwNCiAgICAgICAgICAgICBOZWdQcm9iZSA9IGV4cHJzKHRhcmdldF9EYXRhKVtuZWdfcHJvYmVzLCBdKQ0KU3RhdF9kYXRhX20gPC0gbWVsdChTdGF0X2RhdGEsIG1lYXN1cmUudmFycyA9IGMoIlEzIiwgIk5lZ1Byb2JlIiksDQogICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAiU3RhdGlzdGljIiwgdmFsdWUubmFtZSA9ICJWYWx1ZSIpDQoNCnBsdDEgPC0gZ2dwbG90KFN0YXRfZGF0YV9tLA0KICAgICAgICAgICAgICAgYWVzKHggPSBWYWx1ZSwgZmlsbCA9IFN0YXRpc3RpYykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDQwKSArIHRoZW1lX2J3KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgZmFjZXRfd3JhcCh+QW5ub3RhdGlvbiwgbnJvdyA9IDEpICsgDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAzLCB0eXBlID0gInF1YWwiKSArDQogIGxhYnMoeCA9ICJDb3VudHMiLCB5ID0gIlNlZ21lbnRzLCAjIikNCg0KcGx0MiA8LSBnZ3Bsb3QoU3RhdF9kYXRhLA0KICAgICAgICAgICAgICAgYWVzKHggPSBOZWdQcm9iZSwgeSA9IFEzLCBjb2xvciA9IEFubm90YXRpb24pKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtncmF5IikgKw0KICBnZW9tX3BvaW50KCkgKyBndWlkZXMoY29sb3IgPSAibm9uZSIpICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsNCiAgbGFicyh4ID0gIk5lZ2F0aXZlIFByb2JlIEdlb01lYW4sIENvdW50cyIsIHkgPSAiUTMgVmFsdWUsIENvdW50cyIpDQoNCnBsdDMgPC0gZ2dwbG90KFN0YXRfZGF0YSwNCiAgICAgICAgICAgICAgIGFlcyh4ID0gTmVnUHJvYmUsIHkgPSBRMyAvIE5lZ1Byb2JlLCBjb2xvciA9IEFubm90YXRpb24pKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDEsIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJkYXJrZ3JheSIpICsNCiAgZ2VvbV9wb2ludCgpICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsNCiAgbGFicyh4ID0gIk5lZ2F0aXZlIFByb2JlIEdlb01lYW4sIENvdW50cyIsIHkgPSAiUTMvTmVnUHJvYmUgVmFsdWUsIENvdW50cyIpDQoNCmJ0bV9yb3cgPC0gcGxvdF9ncmlkKHBsdDIsIHBsdDMsIG5yb3cgPSAxLCBsYWJlbHMgPSBjKCJCIiwgIiIpLA0KICAgICAgICAgICAgICAgICAgICAgcmVsX3dpZHRocyA9IGMoMC40MywwLjU3KSkNCnBsb3RfZ3JpZChwbHQxLCBidG1fcm93LCBuY29sID0gMSwgbGFiZWxzID0gYygiQSIsICIiKSkNCg0KYGBgDQoNClEzIG5vcm0gKDc1dGggcGVyY2VudGlsZSkgZm9yIFdUQS9DVEEgd2l0aCBvciB3aXRob3V0IGN1c3RvbSBzcGlrZS1pbnMNCg0KYGBge3IgcTNfbm9ybX0NCnRhcmdldF9EYXRhIDwtIG5vcm1hbGl6ZSh0YXJnZXRfRGF0YSAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fbWV0aG9kID0gInF1YW50IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lyZWRRdWFudGlsZSA9IC43NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCiMsIGRhdGFfdHlwZSA9ICJSTkEiIGRlcHJpY2F0ZWQgYWZ0ZXIgNC4xDQoNCmBgYA0KDQpRdWFudGlsZSBOb3JtYWxpemF0aW9uDQoNCmBgYHtyIHF1YW50aWxlfQ0KcXVhbnRpbGUgPC0gbm9ybWFsaXplLnF1YW50aWxlcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCnJvd25hbWVzKHF1YW50aWxlKSA8LSByb3duYW1lcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCmNvbG5hbWVzKHF1YW50aWxlKSA8LSBjb2xuYW1lcyh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCg0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInF1YW50aWxlX25vcm0iKSA8LSAgcXVhbnRpbGUNCmBgYA0KDQpCYWNrZ3JvdW5kIG5vcm1hbGl6YXRpb24gZm9yIFdUQS9DVEEgd2l0aG91dCBjdXN0b20gc3Bpa2UtaW4NCg0KYGBge3IgYmdfbm9ybX0NCnRhcmdldF9EYXRhIDwtIG5vcm1hbGl6ZSh0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAibmVnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZyb21FbHQgPSAiZXhwcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJuZWdfbm9ybSIpDQoNCiMgLCBkYXRhX3R5cGUgPSAiUk5BIiBkZXByaWNhdGVkIGFmdGVyIDQuMQ0KYGBgDQoNCiMgNS4xIFZpc3VhbGl6ZSB0aGUgZmlyc3QgMTAgc2VnbWVudHMgd2l0aCBlYWNoIG5vcm1hbGl6YXRpb24gbWV0aG9kIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCmBgYHtyIHZpc3VsYWl6ZV9ub3Jtc30NCg0KI0ZpeCB6ZXJvIHZhbHVlcywgd2hpY2ggZ28gdG8gLWluZiBpbiBsb2cgdHJhbnNmb3JtIGluIHN0YW5kYXJkIGJveHBsb3QNCiMgdGVtcCA8LWFzLm1hdHJpeChleHBycygodGFyZ2V0X0RhdGEpWywxOjEwXSkpDQojIGxvbmcgPC0gbWVsdCh0ZW1wKQ0KIyBjb2xuYW1lcyhsb25nKSA8LSBjKCJnZW5lIiwic2VnbWVudCIsImNvdW50IikNCiMgZ2dwbG90KGxvbmcsIGFlcyh4PXNlZ21lbnQseT1jb3VudCkpICsNCiMgICAgIGdlb21fYm94cGxvdChmaWxsPSIjOUVEQUU1IikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJSYXcgY291bnRzIiwgeD0ic2VnbWVudCIsIHkgPSAiQ291bnRzLCBSYXciKQ0KIyANCiMgDQojIHRlbXAgPC1hcy5tYXRyaXgoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMToxMF0sIGVsdCA9ICJxX25vcm0iKSkNCiMgbG9uZyA8LSBtZWx0KHRlbXApDQojIGNvbG5hbWVzKGxvbmcpIDwtIGMoImdlbmUiLCJzZWdtZW50IiwiY291bnQiKQ0KIyBnZ3Bsb3QobG9uZywgYWVzKHg9c2VnbWVudCx5PWNvdW50KSkgKw0KIyAgICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiIzJDQTAyQyIpICsNCiMgICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz1zY2FsZXM6OnBzZXVkb19sb2dfdHJhbnMoYmFzZSA9IDEwKSkgKw0KIyAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygxOjEwKSkgKw0KIyAgICAgbGFicyh0aXRsZT0iUTMgTm9ybSBDb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIFEzIE5vcm1hbGl6ZWQiKQ0KIyANCiMgDQojIHRlbXAgPC1hcy5tYXRyaXgoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMToxMF0sIGVsdCA9ICJuZWdfbm9ybSIpKQ0KIyBsb25nIDwtIG1lbHQodGVtcCkNCiMgY29sbmFtZXMobG9uZykgPC0gYygiZ2VuZSIsInNlZ21lbnQiLCJjb3VudCIpDQojIGdncGxvdChsb25nLCBhZXMoeD1zZWdtZW50LHk9Y291bnQpKSArDQojICAgICBnZW9tX2JveHBsb3QoZmlsbCA9ICIjRkY3RjBFIikgKw0KIyAgICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPXNjYWxlczo6cHNldWRvX2xvZ190cmFucyhiYXNlID0gMTApKSArDQojICAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKDE6MTApKSArDQojICAgICBsYWJzKHRpdGxlPSJOZWcgTm9ybSBDb3VudHMiLCB4PSJzZWdtZW50IiwgeSA9ICJDb3VudHMsIE5lZy4gTm9ybWFsaXplZCIpDQpgYGANCg0KIyMgcmF3IGNvdW50cw0KDQpgYGB7cn0NCmJveHBsb3QoZXhwcnModGFyZ2V0X0RhdGEpWywxOjhdLA0KICAgICAgICBjb2wgPSAiIzlFREFFNSIsIG1haW4gPSAiUmF3IENvdW50cyIsDQogICAgICAgIGxvZyA9ICJ5IiwgbmFtZXMgPSAxOjgsIHhsYWIgPSAiU2VnbWVudCIsDQogICAgICAgIHlsYWIgPSAiQ291bnRzLCBSYXciKQ0KYGBgDQoNCiMjIFEzIG5vcm1hbGl6ZWQgey5hY3RpdmV9DQoNCmBgYHtyfQ0KYm94cGxvdChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjhdLCBlbHQgPSAicV9ub3JtIiksDQogICAgICAgIGNvbCA9ICIjMkNBMDJDIiwgbWFpbiA9ICJRMyBOb3JtIENvdW50cyIsDQogICAgICAgIGxvZyA9ICJ5IiwgbmFtZXMgPSAxOjgsIHhsYWIgPSAiU2VnbWVudCIsDQogICAgICAgIHlsYWIgPSAiQ291bnRzLCBRMyBOb3JtYWxpemVkIikNCmBgYA0KDQojIyBRdWFudGlsZSBub3JtYWxpemVkICh0ZXN0aW5nIHBoYXNlKQ0KDQpgYGB7cn0NCmJveHBsb3QoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMTo4XSwgZWx0ID0gInF1YW50aWxlX25vcm0iKSwNCiAgICAgICAgY29sID0gInBpbmsiLCBtYWluID0gIlF1YW50aWxlIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFF1YW50aWxlIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIE5lZ2F0aXZlIHByb2JlIG5vcm1hbGl6YXRpb24NCg0KYGBge3J9DQpib3hwbG90KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6OF0sIGVsdCA9ICJuZWdfbm9ybSIpLA0KICAgICAgICBjb2wgPSAiI0ZGN0YwRSIsIG1haW4gPSAiTmVnIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIE5lZy4gTm9ybWFsaXplZCIpDQoNCmBgYA0KDQojIDYgVW5zdXBlcnZpc2VkIEFuYWx5c2lzDQoNCiMgNi4xIFVNQVAgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KIyMgMQ0KDQpgYGB7ciB1bWFwLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KY3VzdG9tX3VtYXAgPC0gdW1hcDo6dW1hcC5kZWZhdWx0cw0KY3VzdG9tX3VtYXAkcmFuZG9tX3N0YXRlIDwtIDQyDQojIHJ1biBVTUFQDQoNCnVtYXBfb3V0IDwtDQogIHVtYXAodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICBjb25maWcgPSBjdXN0b21fdW1hcCkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoIlVNQVAxIiwgIlVNQVAyIildIDwtIHVtYXBfb3V0JGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCg0KYmF0Y2hfY29ycmVjdGlvbl9uZWVkZWQgPSBGQUxTRQ0KYGBgDQoNCiMjIDINCg0KYGBge3IgdW1hcDIsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBBTk4zLCBzaGFwZSA9IEFOTjEpKSArDQoNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjIgQmF0Y2ggY29ycmVjdGlvbiB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpJZiB0aGUgZGF0YSBpcyBvYnNlcnZlZCB0byBoYXZlIGEgYmF0Y2ggZWZmZWN0IGl0IGNhbiBiZSBjb3JyZWN0ZWQgd2l0aCB0aGUgbWV0aG9kczogUlVWNCwgTElNTUEsIG9yIENvbWJhdC1TZXEuIEVhY2ggbWV0aG9kIGlzIGRvbmUgYW5kIHRoZSBiZXN0IG9uZSBpcyBwaWNrZWQgYW5kIHVzZWQuDQoNClRoZSBzdGFuZFIgcGFja2FnZSBpcyBzaG9ydCBmb3IgU3BhdGlhbCB0cmFuc2NyaXB0b21pY3MgYW5hbHl6ZXMgYW5kIGRlY29kaW5nIGluIFIsIGl0IGFpbXMgYXQgcHJvdmlkaW5nIGdvb2QgcHJhY3RpY2UgcGlwZWxpbmUgYW5kIHVzZWZ1bCBmdW5jdGlvbnMgZm9yIHVzZXJzIHRvIGFuYWx5emUgTmFub3N0cmluZ+KAmXMgR2VvTXggRFNQIGRhdGEuIEluIHRoZSBOYW5vc3RyaW5n4oCZcyBHZW9NWCBEU1AgcHJvdG9jb2wsIGR1ZSB0byB0aGUgZmFjdCB0aGF0IG9uZSBzbGlkZSBpcyBvbmx5IGJpZyBlbm91Z2ggZm9yIGEgaGFuZGZ1bCBvZiB0aXNzdWUgc2VnbWVudHMgKFJPSXMpLCBpdCBpcyBjb21tb24gdGhhdCB3ZSBzZWUgdGhlIERTUCBkYXRhIGJlaW5nIGNvbmZvdW5kZWQgYnkgdGhlIGJhdGNoIGVmZmVjdCBpbnRyb2R1Y2VkIGJ5IGRpZmZlcmVudCBzbGlkZXMuIEluIG9yZGVyIHRvIGVzdGFibGlzaCBmYWlyIGNvbXBhcmlzb24gYmV0d2VlbiBST0lzIGxhdGVyIG9uLCBpdCBpcyBuZWNlc3NhcnkgdG8gcmVtb3ZlIHRoaXMgYmF0Y2ggZWZmZWN0IGZyb20gdGhlIGRhdGEuIChodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9zdGFuZFIvaW5zdC9kb2Mvc3RhbmRSX2ludHJvZHVjdGlvbi5odG1sKQ0KDQpGb3IgUlVWNCBjb3JyZWN0aW9uLCB0aGUgZnVuY3Rpb24gaXMgcmVxdWlyaW5nIDMgcGFyYW1ldGVycyBvdGhlciB0aGFuIHRoZSBpbnB1dCBvYmplY3QsIGluY2x1ZGluZyBmYWN0b3JzOiB0aGUgZmFjdG9yIG9mIGludGVyZXN0LCBpLmUuIHRoZSBiaW9sb2dpY2FsIHZhcmlhdGlvbiB3ZSBwbGFuIHRvIGtlZXA7IE5DR3M6IHRoZSBsaXN0IG9mIG5lZ2F0aXZlIGNvbnRyb2wgZ2VuZXMgZGV0ZWN0ZWQgdXNpbmcgdGhlIGZ1bmN0aW9uIGZpbmROQ0dzOyBhbmQgazogaXMgdGhlIG51bWJlciBvZiB1bndhbnRlZCBmYWN0b3JzIHRvIHVzZSwgaW4gdGhlIFJVViBkb2N1bWVudGF0aW9uLCBpdCBpcyBzdWdnZXN0IHRoYXQgd2Ugc2hvdWxkIHVzZSB0aGUgc21hbGxlc3QgayBvbmNlIHdlIGRvbuKAmXQgb2JzZXJ2ZSB0ZWNobmljYWwgdmFyaWF0aW9uIGluIHRoZSBkYXRhLg0KDQpBbm90aGVyIG9wdGlvbiBpcyBzZXQgdGhlIHBhcmFtZXRlciBtZXRob2QgdG8g4oCcTGltbWHigJ0sIHdoaWNoIHVzZXMgdGhlIHJlbW92ZSBiYXRjaCBjb3JyZWN0aW9uIG1ldGhvZCBmcm9tIGxpbW1hLiBJbiB0aGlzIG1vZGUsIHRoZSBmdW5jdGlvbiBpcyByZXF1aXJpbmcgMiBwYXJhbWV0ZXJzLCBpbmNsdWRpbmcgYmF0Y2g6IGEgdmVjdG9yIHRoYXQgaW5kaWNhdGluZyBiYXRjaGVzIGZvciBhbGwgc2FtcGxlczsgYW5kIGRlc2lnbjogYSBkZXNpZ24gbWF0cml4IHdoaWNoIGlzIGdlbmVyYXRlZCBieSBtb2RlbC5tYXRyaXgsIGluIHRoZSBkZXNpZ24gbWF0cml4LCBhbGwgYmlvbG9naWNhbGx5LXJlbGV2YW50IGZhY3RvcnMgc2hvdWxkIGJlIGluY2x1ZGVkLg0KDQpDb21CYXQtc2VxIGlzIGEgYmF0Y2ggZWZmZWN0IGFkanVzdG1lbnQgdG9vbCBmb3IgYnVsayBSTkEtc2VxIGNvdW50IGRhdGEuIEl0IGlzIGFuIGltcHJvdmVkIG1vZGVsIGJhc2VkIG9uIHRoZSBwb3B1bGFyIENvbUJhdCwgdG8gYWRkcmVzcyBpdHMgbGltaXRhdGlvbnMgdGhyb3VnaCBub3ZlbCBtZXRob2RzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IgUk5BLVNlcSBzdHVkaWVzLiBDb21CYXQtc2VxIHRha2VzIHVudHJhbnNmb3JtZWQsIHJhdyBjb3VudCBtYXRyaXggYXMgaW5wdXQuIFNhbWUgYXMgQ29tQmF0LCBpdCByZXF1aXJlcyBhIGtub3duIGJhdGNoIHZhcmlhYmxlLiAoaHR0cHM6Ly9naXRodWIuY29tL3poYW5neXVxaW5nL0NvbUJhdC1zZXEpDQoNCiMjIFNldCB1cCBzdGFuZFIgb2JqZWN0DQoNCmBgYHtyIGJhdGNoIGNvcnJlY3Rpb24sIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpjb3VudF9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KDQpzYW1wbGVfZ2VvbXggPC0gdGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGENCnNhbXBsZV9nZW9teCRyb2kgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGEkcm9pDQpzYW1wbGVfZ2VvbXggPC0gYXMuZGF0YS5mcmFtZShzYW1wbGVfZ2VvbXgpDQpzYW1wbGVfZ2VvbXgkU2FtcGxlX0lEIDwtIHJvd25hbWVzKHNhbXBsZV9nZW9teCkNCnNhbXBsZV9nZW9teCRTZWdtZW50RGlzcGxheU5hbWUgPC0gcGFzdGUoc2FtcGxlX2dlb214JGBzY2FuIG5hbWVgLCBzYW1wbGVfZ2VvbXgkcm9pLCBzYW1wbGVfZ2VvbXgkc2VnbWVudCwgc2VwID0gIiB8ICIpDQpzYW1wbGVfZ2VvbXgkUk9JQ29vcmRpbmF0ZVggPC0gMQ0Kc2FtcGxlX2dlb214JFJPSUNvb3JkaW5hdGVZIDwtIDENCg0KDQpmZWF0dXJlX2dlb214IDwtIGZEYXRhKERhdGEpDQpmZWF0dXJlX2dlb214IDwtIGZlYXR1cmVfZ2VvbXhbYygiUlRTX0lEIiwgIlRhcmdldE5hbWUiLCAiUHJvYmVJRCIsICJOZWdhdGl2ZSIpXQ0KZmVhdHVyZV9nZW9teCA8LSBhcy5kYXRhLmZyYW1lKGZlYXR1cmVfZ2VvbXgpDQpyb3duYW1lcyhmZWF0dXJlX2dlb214KSA8LSBOVUxMDQoNCm1hdGNoaW5nIDwtIHNhbXBsZV9nZW9teCRTZWdtZW50RGlzcGxheU5hbWVbc2FtcGxlX2dlb214JFNhbXBsZV9JRCAlaW4lIGNvbG5hbWVzKGNvdW50X2dlb214KV0NCmNvbG5hbWVzKGNvdW50X2dlb214KSA8LSBtYXRjaGluZw0KY291bnRfZ2VvbXgkVGFyZ2V0TmFtZSA8LSByb3duYW1lcyhjb3VudF9nZW9teCkNCnJvd25hbWVzKGNvdW50X2dlb214KSA8LSBOVUxMDQoNCnNwZSA8LSByZWFkR2VvTXgoY291bnRfZ2VvbXgsIHNhbXBsZV9nZW9teCwgZmVhdHVyZUFubm9GaWxlID0gZmVhdHVyZV9nZW9teCwgaGFzTmVnUHJvYmUgPSBUUlVFKQ0KDQpjb2xEYXRhKHNwZSkkcmVnaW9ucyA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJEFOTjIsIl8iLGNvbERhdGEoc3BlKSRBTk4xKSB8PiANCiAgKFwoLikgZ3N1YigiX0dlb21ldHJpYyBTZWdtZW50IiwiIiwuKSkoKSB8Pg0KICBwYXN0ZTAoIl8iLGNvbERhdGEoc3BlKSRwYXRob2xvZ3kpIHw+DQogIChcKC4pIGdzdWIoIl9OQSIsIl9ucyIsLikpKCkNCg0KY29sRGF0YShzcGUpJHJlZ2lvbnMgPC0gcGFzdGUwKGNvbERhdGEoc3BlKSRBTk4yLCJfIixjb2xEYXRhKHNwZSkkQU5OMSkgfD4gDQogIChcKC4pIGdzdWIoIl9HZW9tZXRyaWMgU2VnbWVudCIsIiIsLikpKCkgfD4NCiAgcGFzdGUwKCJfIixjb2xEYXRhKHNwZSkkcGF0aG9sb2d5KSB8Pg0KICAoXCguKSBnc3ViKCJfTkEiLCJfbnMiLC4pKSgpDQpjb2xEYXRhKHNwZSkkYmlvbG9neSA8LSBwYXN0ZTAoY29sRGF0YShzcGUpJHJlZ2lvbnMpDQpgYGANCg0KIyMgU2VlIG9wdGltYWwgayB2YWx1ZSBmb3IgUlVWNA0KDQpgYGB7ciwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnNwZSA8LSBmaW5kTkNHcyhzcGUsIGJhdGNoX25hbWUgPSAic2xpZGVfbmFtZSIsIHRvcF9uID0gNTAwKQ0KZmluZEJlc3RLKHNwZSwgbWF4SyA9IDEwLCBmYWN0b3Jfb2ZfaW50ID0gImJpb2xvZ3kiLCBOQ0dzID0gbWV0YWRhdGEoc3BlKSROQ0dzLCBmYWN0b3JfYmF0Y2ggPSAic2xpZGVfbmFtZSIpDQpgYGANCg0KIyMgUlVWNA0KDQpgYGB7ciBydXY0IGJhdGNoLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xMCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnJ1djQgPC0gZ2VvbXhCYXRjaENvcnJlY3Rpb24oc3BlLCBmYWN0b3JzID0gImJpb2xvZ3kiLCANCiAgICAgICAgICAgICAgICAgICBOQ0dzID0gbWV0YWRhdGEoc3BlKSROQ0dzLCBrID0gMSkNCg0KcGxvdFBhaXJQQ0EocnV2NCwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gcmVnaW9ucywgdGl0bGUgPSAiUlVWNCByZW1vdmVCYXRjaCIpDQoNCnBsb3RSTEV4cHIocnV2NCwgYXNzYXkgPSAyLCBjb2xvciA9IGBzbGlkZV9uYW1lYCkgKyBnZ3RpdGxlKCJSVVY0IHJlbW92ZUJhdGNoIikNCmBgYA0KDQojIyBMaW1tYQ0KDQpgYGB7ciBsaW1tYSBiYXRjaCwgIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTEwLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KbGltbWEgPC0gZ2VvbXhCYXRjaENvcnJlY3Rpb24oc3BlLA0KICAgICAgICAgICAgICAgICAgICAgICBiYXRjaCA9IGNvbERhdGEoc3BlKSRgc2xpZGVfbmFtZWAsIG1ldGhvZCA9ICJMaW1tYSIsDQogICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IG1vZGVsLm1hdHJpeCh+IDAgKyBBTk4xICsgcmVnaW9ucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gY29sRGF0YShzcGUpKSkNCg0KcGxvdFBhaXJQQ0EobGltbWEsIGFzc2F5ID0gMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IHJlZ2lvbnMsIHRpdGxlID0gIkxpbW1hIHJlbW92ZUJhdGNoIikNCg0KcGxvdFJMRXhwcihsaW1tYSwgYXNzYXkgPSAyLCBjb2xvciA9IHNsaWRlX25hbWUpICsgZ2d0aXRsZSgiTGltbWEgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIENvbWJhdFNlcQ0KDQpgYGB7ciwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCmFkanVzdGVkIDwtIENvbUJhdF9zZXEodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1siZXhwcnMiXV0sIGJhdGNoPXRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGVfbmFtZSJdXSwgZ3JvdXA9dGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGFbWyJBTk4yIl1dKQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImNvbWJhdCIpIDwtICBhZGp1c3RlZA0KDQp1bWFwX291dDIgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJjb21iYXQiKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQyJGxheW91dFssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyLCBjb2xvciA9IHNsaWRlX25hbWUsIHNoYXBlID0gQU5OMSkpICsNCg0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJDb21iYXRTZXEgcmVtb3ZlQmF0Y2giKQ0KYGBgDQoNCiMjIENvbXBhcmUgbGltbWEgdnMgUlVWNA0KDQpgYGB7ciBjb21wYXJlIGJhdGNoLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0Kc3BlX2xpc3QgPC0gbGlzdChzcGUsIHJ1djQsIGxpbW1hKQ0KDQpwbG90Q2x1c3RlckV2YWxTdGF0cyhzcGVfbGlzdCA9IHNwZV9saXN0LA0KICAgICAgICAgICAgICAgICAgICAgYmlvX2ZlYXR1cmVfbmFtZSA9ICJyZWdpb25zIiwNCiAgICAgICAgICAgICAgICAgICAgIGJhdGNoX2ZlYXR1cmVfbmFtZSA9ICJzbGlkZV9uYW1lIiwNCiAgICAgICAgICAgICAgICAgICAgIGRhdGFfbmFtZXMgPSBjKCJSYXciLCJSVVY0IiwiTGltbWEiKSkNCmBgYA0KDQojIyBBZGQgYmF0Y2ggY29ycmVjdGlvbiByZXN1bHQgdG8gdGFyZ2V0X0RhdGENCg0KYGBge3IgYWRkIGJhdGNoIGNvcnJlY3Rpb24sIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpuZWdfcHJvYmVzX3NhdmUgPC0gdChhcy5tYXRyaXgodGFyZ2V0X0RhdGFAYXNzYXlEYXRhJHFfbm9ybVsiTmVnUHJvYmUtV1RYIixdKSkNCnJvd25hbWVzKG5lZ19wcm9iZXNfc2F2ZSkgPC0gIk5lZ1Byb2JlLVdUWCINCg0KIyBEZXBlbmRpbmcgb24gY29ycmVjdCBtZXRob2QsIGNoYW5nZSB0aGUgd29yZCAibGltbWEiIHRvICJydXY0IiBvciB2aWNlIHZlcnNhLg0KbGltbWEgPC1saW1tYUBhc3NheXNAZGF0YUBsaXN0RGF0YVtbImxvZ2NvdW50cyJdXQ0KbGltbWEgPC0gcmJpbmQobGltbWEsIG5lZ19wcm9iZXNfc2F2ZSkNCmNvbG5hbWVzKGxpbW1hKSA8LSBjb2xuYW1lcyhhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbInFfbm9ybSJdXSkpDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibGltbWEiKSA8LSAgbGltbWENCg0KcnV2NCA8LXJ1djRAYXNzYXlzQGRhdGFAbGlzdERhdGFbWyJsb2djb3VudHMiXV0NCnJ1djQgPC0gcmJpbmQocnV2NCwgbmVnX3Byb2Jlc19zYXZlKQ0KY29sbmFtZXMocnV2NCkgPC0gY29sbmFtZXMoYXMuZGF0YS5mcmFtZSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJxX25vcm0iXV0pKQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInJ1djQiKSA8LSAgcnV2NA0KYGBgDQoNCiMjIENob29zZSBtZXRob2Qgey5hY3RpdmV9DQoNCmBgYHtyfQ0KIyBjaG9vc2UgbWV0aG9kIHRvIHJlcGxhY2UgcV9ub3JtIG9yIHNldCBpdCB0byAiIg0KbWV0aG9kIDwtICIiDQoNCiMgcmVwbGFjZSBxX25vcm0gd2l0aCBjaG9zZW4gbWV0aG9kDQppZiAoIW1ldGhvZCA9PSAiIikgew0KICAjIHNhdmUgb3JnaW5hbCBxX25vcm0NCiAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gIm9yaWdpbmFsIikgPC0gIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJxX25vcm0iKQ0KICANCiAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInFfbm9ybSIpIDwtICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSBtZXRob2QpDQogIHByaW50KHBhc3RlKCJCYXRjaCBjb3JyZWN0aW9uIG1ldGhvZDoiLCBtZXRob2QpKQ0KfSBlbHNlIHtwcmludCgiTm8gYmF0Y2ggY29ycmVjdGlvbiBuZWVkZWQiKX0NCmBgYA0KDQojIyBVbWFwIGFmdGVyIGJhdGNoIGNvcnJlY3Rpb24NCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTgsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQp1bWFwX291dCA8LQ0KICB1bWFwKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpKSksDQogICAgICAgY29uZmlnID0gY3VzdG9tX3VtYXApDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJVTUFQMSIsICJVTUFQMiIpXSA8LSB1bWFwX291dCRsYXlvdXRbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IEFOTjEpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMgNi4zIFJ1biB0U05FIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCk9uZSBjb21tb24gYXBwcm9hY2ggdG8gdW5kZXJzdGFuZGluZyBoaWdoLXBsZXggZGF0YSBpcyBkaW1lbnNpb24NCnJlZHVjdGlvbi4gVHdvIGNvbW1vbiBtZXRob2RzIGFyZSBVTUFQIGFuZCB0U05FLCB3aGljaCBhcmUNCm5vbi1vcnRob2dvbmFsbHkgY29uc3RyYWluZWQgcHJvamVjdGlvbnMgdGhhdCBjbHVzdGVyIHNhbXBsZXMgYmFzZWQgb24NCm92ZXJhbGwgZ2VuZSBleHByZXNzaW9uLiBJbiB0aGlzIHN0dWR5LCB3ZSBzZWUgYnkgZWl0aGVyIFVNQVAgKGZyb20gdGhlDQp1bWFwIHBhY2thZ2UpIG9yIHRTTkUgKGZyb20gdGhlIFJ0c25lIHBhY2thZ2UpDQoNCiMjIDENCg0KYGBge3IgdFNORSwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9OH0NCnRzbmVfb3V0IDwtDQogIFJ0c25lKHQobG9nMihhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhICwgZWx0ID0gInFfbm9ybSIpKSksDQogICAgICAgIHBlcnBsZXhpdHkgPSBuY29sKHRhcmdldF9EYXRhKSouMTUpDQpwRGF0YSh0YXJnZXRfRGF0YSlbLCBjKCJ0U05FMSIsICJ0U05FMiIpXSA8LSB0c25lX291dCRZWywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IHRTTkUxLCB5ID0gdFNORTIsIHNoYXBlID0gc2xpZGVfbmFtZSwgY29sb3IgPSBBTk4yKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyAyDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KdHNuZV9vdXQgPC0NCiAgUnRzbmUodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICAgcGVycGxleGl0eSA9IG5jb2wodGFyZ2V0X0RhdGEpKi4xNSkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoInRTTkUxIiwgInRTTkUyIildIDwtIHRzbmVfb3V0JFlbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gdFNORTEsIHkgPSB0U05FMiwgY29sb3IgPSBBTk4zLCBzaGFwZSA9IEFOTjEpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMgNi40IENsdXN0ZXJpbmcgaGlnaCBDViBHZW5lcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpBbm90aGVyIGFwcHJvYWNoIHRvIGV4cGxvcmUgdGhlIGRhdGEgaXMgdG8gY2FsY3VsYXRlIHRoZSBjb2VmZmljaWVudCBvZg0KdmFyaWF0aW9uICgkQ1YkKSBmb3IgZWFjaCBnZW5lICgkZyQpIHVzaW5nIHRoZSBmb3JtdWxhDQokQ1ZfZz1TRF9nL21lYW5fZyQuIFdlIHRoZW4gaWRlbnRpZnkgZ2VuZXMgd2l0aCBoaWdoIENWcyB0aGF0IHNob3VsZA0KaGF2ZSBsYXJnZSBkaWZmZXJlbmNlcyBhY3Jvc3MgdGhlIHZhcmlvdXMgcHJvZmlsZWQgc2VnbWVudHMuIFRoaXMNCnVuYmlhc2VkIGFwcHJvYWNoIGNhbiByZXZlYWwgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFjcm9zcyB0aGUgc3R1ZHkuDQoNCldlIHBsb3QgdGhlIHJlc3VsdHMgdXNpbmcgdW5zdXBlcnZpc2VkIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLA0KZGlzcGxheWVkIGFzIGEgaGVhdG1hcC4NCg0KYGBge3IgY2x1c3RlcmluZzF9DQojIGNyZWF0ZSBhIGxvZzIgdHJhbnNmb3JtIG9mIHRoZSBkYXRhIGZvciBhbmFseXNpcw0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0NCiAgYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsIDIsIEZVTiA9IGxvZywgYmFzZSA9IDIsIGVsdCA9ICJxX25vcm0iKQ0KDQojIGNyZWF0ZSBDViBmdW5jdGlvbg0KY2FsY19DViA8LSBmdW5jdGlvbih4KSB7c2QoeCkgLyBtZWFuKHgpfQ0KQ1ZfZGF0IDwtIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsIE1BUkdJTiA9IDEsIGNhbGNfQ1YpDQojIHNob3cgdGhlIGhpZ2hlc3QgQ0QgZ2VuZXMgYW5kIHRoZWlyIENWIHZhbHVlcw0Kc29ydChDVl9kYXQsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjVdDQpgYGANCg0KIyMgVGFibGUgb2YgQ1YgdmFsdWVzDQoNCmBgYHtyfQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKENWX2RhdCksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICksIGNhcHRpb24gPSAiQ1YgdmFsdWVzIG9mIGdlbmVzIiANCikgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiQ1ZfZGF0IiksIGRpZ2l0cz0zKQ0KDQpgYGANCg0KIyMgSGVhdG1hcCBnZW5lcyBpbiB0aGUgdG9wIDNyZCBvZiB0aGUgQ1YgdmFsdWVzIHsuYWN0aXZlfQ0KDQpgYGB7ciBDVmhlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTE1fQ0KR09JIDwtIG5hbWVzKENWX2RhdClbQ1ZfZGF0ID4gcXVhbnRpbGUoQ1ZfZGF0LCAwLjc1KV0NCg0KcGhlYXRtYXAoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtHT0ksIF0sIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICAgc2NhbGUgPSAicm93IiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGRyb3BfbGV2ZWxzID0gVFJVRSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoInB1cnBsZTMiLCAiYmxhY2siLCAieWVsbG93MiIpKSgxMjApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQpgYGANCg0KYGBge3IgbG9nX3RyYW5zZm9ybX0NCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQpsb2dfcSA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAibG9nX3EiKSkNCiNiYXRjaCA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiYmF0Y2giKSkNCmBgYA0KDQojIDYuNS4wIENyZWF0ZSBzdWJzZXQgb2YgZGF0YQ0KDQpgYGB7ciBkZWZpbmVfYWN0aXZlX2FvaXN9DQojIGRldGVybWluZSBBT0lzIHRvIHVzZQ0KI2FjdGl2ZV9hb2lzPC1yb3duYW1lcyhhbm4pW2FubiRwYXRpZW50PT0icDQiXQ0KYWN0aXZlX2FvaXM8LSBuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKQ0KYGBgDQoNCiMgNi41LjEgQ2x1c3RlcmluZyBoaWdoIENWIGdlbmVzIGZvciBzdWJzZXQgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KQ2FsY3VsYXRpbmcgQ1YgdmFsdWVzDQoNCmBgYHtyIGNsdXN0ZXJpbmdfc3Vic2V0LCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xNX0NCiMgY3JlYXRlIGEgbG9nMiB0cmFuc2Zvcm0gb2YgdGhlIGRhdGEgZm9yIGFuYWx5c2lzDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibG9nX3EiKSA8LQ0KICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQoNCiMgY3JlYXRlIENWIGZ1bmN0aW9uDQpjYWxjX0NWIDwtIGZ1bmN0aW9uKHgpIHtzZCh4KSAvIG1lYW4oeCl9DQpDVl9kYXQgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGFbLGFjdGl2ZV9hb2lzXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiLCBNQVJHSU4gPSAxLCBjYWxjX0NWKQ0KDQpgYGANCg0KIyMgVGFibGUgb2YgQ1YgdmFsdWVzDQoNCmBgYHtyfQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKENWX2RhdCksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICksIGNhcHRpb24gPSAiQ1YgdmFsdWVzIG9mIGdlbmVzIiANCikgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiQ1ZfZGF0IiksIGRpZ2l0cz0zKQ0KDQpgYGANCg0KIyMgSGVhdG1hcCBvbiBvZiBzdWJzZXQsIGdlbmVzIGluIHRoZSB0b3AgM3JkIG9mIHRoZSBDViB2YWx1ZXMgey5hY3RpdmV9DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xNX0NCiMgSWRlbnRpZnkgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcw0KR09JIDwtIG5hbWVzKENWX2RhdClbQ1ZfZGF0ID4gcXVhbnRpbGUoQ1ZfZGF0LCAwLjc1KV0NCnBoZWF0bWFwKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbR09JLGFjdGl2ZV9hb2lzIF0sIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICBmb250c2l6ZV9yb3cgPSA1LA0KICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICBhbm5vdGF0aW9uX2NvbCA9DQogICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCmBgYA0KDQojIDcuMSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbg0KDQohW10oTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9raWRuZXlfc3RydWN0dXJlLnBuZykNCg0KIyMgdC10ZXN0DQoNCmBgYHtyIHR0ZXN0fQ0KI2djKCkNCnBsb3RzPC1saXN0KCkNCnRhYmxlczwtbGlzdCgpDQpsYWJlbHM8LWxpc3QoKQ0KdGVzdDwtInR0ZXN0Ig0KbXRjPC0iQlkiDQojb3B0aW9uczogImhvbG0iICAgICAgICJob2NoYmVyZyIgICAiaG9tbWVsIiAgICAgImJvbmZlcnJvbmkiICJCSCIgICAgICAgICAiQlkiICAgICAgICAgImZkciIgDQpjb3VudGVyPTENCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCmZvcihyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICBmb3IgKGFjdGl2ZV9ncm91cDEgaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xKSkgew0KICAgIGZvciAoYWN0aXZlX2dyb3VwMiBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEpKSB7DQogICAgICANCiAgICAgICNzdXByZXNzIHJlZHVuY2FudCBjb21wYXJlcw0KICAgICAgaWYoYWN0aXZlX2dyb3VwMT09YWN0aXZlX2dyb3VwMikge25leHR9DQogICAgICBjb21wPC1wYXN0ZShzb3J0KGMocmVnaW9uLCBhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgIGlmKGNvbXAgJWluJSBjb21wc19kZiRjb21wKSB7bmV4dH0NCiAgICAgIHRlbXBfZGY8LWRhdGEuZnJhbWUoY29tcD1jb21wICx2YWw9MSkNCiAgICAgIGNvbXBzX2RmPC1yYmluZChjb21wc19kZix0ZW1wX2RmKQ0KICAgICAgDQogICAgICBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICBncm91cDE8LWxvZ19xWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMT09YWN0aXZlX2dyb3VwMSAmIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zPT1yZWdpb25dXQ0KICAgICAgZ3JvdXAyPC1sb2dfcVssbmFtZXMoYXMuZGF0YS5mcmFtZShhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLCBlbHQ9ICJleHBycyIpKSlbcERhdGEodGFyZ2V0X0RhdGEpJEFOTjE9PWFjdGl2ZV9ncm91cDIgJiBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMz09cmVnaW9uXV0NCiAgICAgIA0KICAgICAgI3J1biB0X3Rlc3RzICANCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcSwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCiAgICAgIA0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KICAgICAgDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQogICAgICANCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZShhcHBseShsb2dfcSwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pKSkpDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgICNwYXN0ZSgiRkMiLGFjdGl2ZV9ncm91cDEsIiAvICIsYWN0aXZlX2dyb3VwMikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQogICAgICANCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQogICAgICANCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAxIg0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJHJhd19wX3ZhbHVlIDwgMC4wNV0gPC0gIlAgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQogICAgICByZXN1bHRzJENvbG9yW2FicyhyZXN1bHRzJEZDKSA8MV0gPC0gIk5TIG9yIEZDIDwgMSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDEiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KICAgICAgDQogICAgICAjdnVsY2Fub3Bsb3QNCiAgICAgIA0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQogICAgICANCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MjBdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1ssICdHZW5lJ11bb3JkZXIocmVzdWx0c1ssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MjBdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgICAgIA0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICANCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChyYXdfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMSwgLTEpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIGxhYnMoeCA9IHBhc3RlKCJFbnJpY2hlZCBpbiIsIGFjdGl2ZV9ncm91cDIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsIGFjdGl2ZV9ncm91cDEpLA0KICAgICAgICAgICAgIHkgPSAiU2lnbmlmaWNhbmNlLCAtbG9nMTAoUCkiLA0KICAgICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEZEUiA8IDAuMDVgID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuNWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjA1KSkpICsNCiAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQocmVzdWx0cywgKC0xPkZDfCBGQz4xKSAmIEZEUiA8IDAuMDUgJiBHZW5lICVpbiUgdG9wX2cpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZT0zLjUsDQogICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEwKSArDQogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgIGdndGl0bGUocGFzdGUoImNsYXNzOiAiLCByZWdpb24sIiAtICIsIHRlc3QsIG10YywibXVsdGl0ZXN0IGNvcnIiKSkNCiAgICAgIA0KICAgICAgI3N0b3JlIHRhYmxlcyBmb3IgZGlzcGxheSBsYXRlcg0KICAgICAgdGFibGVzW1tjb3VudGVyXV08LXJlc3VsdHMNCiAgICAgIA0KICAgICAgY291bnRlciA9IGNvdW50ZXIrMQ0KICAgICAgI2RhdGF0YWJsZShzdWJzZXQocmVzdWx0cywgR2VuZSAlaW4lIEdPSSksIHJvd25hbWVzPUZBTFNFLGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikpDQogICAgfQ0KICB9DQp9DQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHMsbmNvbD0yKQ0KYGBgDQoNCmBgYHtyIGR1bXBfdGFibGVzLHJlc3VsdHM9J2FzaXMnfQ0KI3N0cmFuZ2x5IGRvZXMgbm90IGFwcGVhciBpbiBodG1sIG91dHB1dCAtPiBjaHVja3MgYXJlIGxpbWl0ZWQgdG8gb25lIGRhdGF0YWJsZSBwZXIgY2h1Y2suIFRoZSBodG1sdG9vbHMgYXJlIGEgd2F5IGFyb3VuZCB0aGlzIGJ1dCBkb2VzIHNob3cgdGhlbSBzdGFja2VkIGluIGEgYm94Lg0KcmVzdWx0c190YWJsZXMgPC0gaHRtbHRvb2xzOjp0YWdMaXN0KCkNCmZvciAoYyBpbiAoMjpjb3VudGVyLTEpKSB7DQogICNHZW5lICVpbiUgR09JDQogIHJlc3VsdHNfdGFibGVzW1tjXV0gPC0gZGF0YXRhYmxlKHN1YnNldCh0YWJsZXNbW2NdXSwgQ29sb3IgPT0gYygiRkRSIDwgMC4wMDEiLCAgIlAgPCAwLjA1IikpLCANCiAgICAgICAgICAgcm93bmFtZXM9RkFMU0UsDQogICAgICAgICAgIGV4dGVuc2lvbnMgPSBjKCdCdXR0b25zJyksIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICAgKSwgaGVpZ2h0ID0gNTUwLA0KICAgICAgICAgICBjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgbGFiZWxzW1tjXV0pLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygicmF3X3BfdmFsdWUiLCJhZGpfcF92YWx1ZSIsIkZEUiIsIkZDIiksIGRpZ2l0cz0zKQ0KICAjY2F0KCdcblxuPCEtLSAtLT5cblxuJykNCn0gICAgICAgICAgICANCnJlc3VsdHNfdGFibGVzDQpgYGANCg0KYGBge3J9DQpwcmludCgiIikgIyBmb3IgbGF5b3V0IG9mIG5leHQgc2VjdGlvbg0KYGBgDQoNCiMgNy4yIERFIGFuYWx5c2lzIHdpdGggTE1NDQoNCkEgY29tbW9uIHN0YXRpc3RpY2FsIGFwcHJvYWNoIGlzIHRvIHVzZSBhIGxpbmVhciBtaXhlZC1lZmZlY3QgbW9kZWwNCihMTU0pLiBUaGUgTE1NIGFsbG93cyB0aGUgdXNlciB0byBhY2NvdW50IGZvciB0aGUgc3Vic2FtcGxpbmcgcGVyDQp0aXNzdWU7IGluIG90aGVyIHdvcmRzLCB3ZSBhZGp1c3QgZm9yIHRoZSBmYWN0IHRoYXQgdGhlIG11bHRpcGxlIHJlZ2lvbnMNCm9mIGludGVyZXN0IHBsYWNlZCBwZXIgdGlzc3VlIHNlY3Rpb24gYXJlIG5vdCBpbmRlcGVuZGVudCBvYnNlcnZhdGlvbnMsDQphcyBpcyB0aGUgYXNzdW1wdGlvbiB3aXRoIG90aGVyIHRyYWRpdGlvbmFsIHN0YXRpc3RpY2FsIHRlc3RzLiBUaGUNCmZvcm11bGF0aW9uIG9mIHRoZSBMTU0gbW9kZWwgZGVwZW5kcyBvbiB0aGUgc2NpZW50aWZpYyBxdWVzdGlvbiBiZWluZw0KYXNrZWQuDQoNCk92ZXJhbGwsIHRoZXJlIGFyZSB0d28gZmxhdm9ycyBvZiB0aGUgTE1NIG1vZGVsIHdoZW4gdXNlZCB3aXRoIEdlb014DQpkYXRhOiBpKSB3aXRoIGFuZCBpaSkgd2l0aG91dCByYW5kb20gc2xvcGUuDQoNCldoZW4gY29tcGFyaW5nIGZlYXR1cmVzIHRoYXQgY28tZXhpc3QgaW4gYSBnaXZlbiB0aXNzdWUgc2VjdGlvbiwgYQ0KcmFuZG9tIHNsb3BlIGlzIGluY2x1ZGVkIGluIHRoZSBMTU0gbW9kZWwuIFdoZW4gY29tcGFyaW5nIGZlYXR1cmVzIHRoYXQNCmFyZSBtdXR1YWxseSBleGNsdXNpdmUgaW4gYSBnaXZlbiB0aXNzdWUgc2VjdGlvbiB0aGUgTE1NIG1vZGVsIGRvZXMgbm90DQpyZXF1aXJlIGEgcmFuZG9tIHNsb3BlLg0KDQpNb3N0bHksIHdlIHVzZSBwYXRpZW50L3NhbXBsZSBhcyBSYW5kb20gSW50ZXJjZXB0IHdoZW4gdGhleSBhcmUgY29tYmluZWQNCm9uIHNsaWRlcy4NCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL0xNTV9zZXR1cC5wbmcpDQoNCmBgYHtyIExNTSByZWdpb259DQojIGNvbnZlcnQgdGVzdCB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RDbGFzcyA8LSBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkNCnBEYXRhKHRhcmdldF9EYXRhKVtbInNsaWRlIl1dIDwtIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSlbWyJzbGlkZV9uYW1lIl1dKQ0KI2Fzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCg0KIyBydW4gTE1NOg0KIyBmb3JtdWxhIGZvbGxvd3MgY29udmVudGlvbnMgZGVmaW5lZCBieSB0aGUgbG1lNCBwYWNrYWdlDQpsbW1fcmVzdWx0cyA8LSBjKCkNCmZvciAocmVnaW9uIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMykpIHsNCiAgICBpbmQgPC0gcERhdGEodGFyZ2V0X0RhdGEpJEFOTjMgPT0gcmVnaW9uDQoNCiAgICBtaXhlZE91dG1jIDwtDQogICAgICAgIG1peGVkTW9kZWxERSh0YXJnZXRfRGF0YVssaW5kXSwNCiAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIsDQogICAgICAgICAgICAgICAgICAgICBtb2RlbEZvcm11bGEgPSB+IHRlc3RDbGFzcyArICgxIHwgc2xpZGUpLA0KICAgICAgICAgICAgICAgICAgICAgZ3JvdXBWYXIgPSAidGVzdENsYXNzIiwNCiAgICAgICAgICAgICAgICAgICAgIG5Db3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLA0KICAgICAgICAgICAgICAgICAgICAgbXVsdGlDb3JlID0gVFJVRSkNCg0KICAgICMgZm9ybWF0IHJlc3VsdHMgYXMgZGF0YS5mcmFtZQ0KICAgIHJfdGVzdCA8LSBkby5jYWxsKHJiaW5kLCBtaXhlZE91dG1jWyJsc21lYW5zIiwgXSkNCiAgICB0ZXN0cyA8LSByb3duYW1lcyhyX3Rlc3QpDQogICAgcl90ZXN0IDwtIGFzLmRhdGEuZnJhbWUocl90ZXN0KQ0KICAgIHJfdGVzdCRDb250cmFzdCA8LSB0ZXN0cw0KDQogICAgIyB1c2UgbGFwcGx5IGluIGNhc2UgeW91IGhhdmUgbXVsdGlwbGUgbGV2ZWxzIG9mIHlvdXIgdGVzdCBmYWN0b3IgdG8NCiAgICAjIGNvcnJlY3RseSBhc3NvY2lhdGUgZ2VuZSBuYW1lIHdpdGggaXQncyByb3cgaW4gdGhlIHJlc3VsdHMgdGFibGUNCiAgICByX3Rlc3QkR2VuZSA8LQ0KICAgICAgICB1bmxpc3QobGFwcGx5KGNvbG5hbWVzKG1peGVkT3V0bWMpLA0KICAgICAgICAgICAgICAgICAgICAgIHJlcCwgbnJvdyhtaXhlZE91dG1jWyJsc21lYW5zIiwgXVtbMV1dKSkpDQogICAgcl90ZXN0JFN1YnNldCA8LSByZWdpb24NCiAgICByX3Rlc3QkRkRSIDwtIHAuYWRqdXN0KHJfdGVzdCRgUHIoPnx0fClgLCBtZXRob2QgPSAiZmRyIikNCiAgICByX3Rlc3QgPC0gcl90ZXN0WywgYygiR2VuZSIsICJTdWJzZXQiLCAiQ29udHJhc3QiLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJQcig+fHR8KSIsICJGRFIiKV0NCiAgICANCiAgICBsbW1fcmVzdWx0cyA8LSByYmluZChsbW1fcmVzdWx0cywgcl90ZXN0KQ0KfQ0KYGBgDQoNCiMgNy4zIFZ1bGNhbm9wbG90ICsgdGFibGUgb2YgTE1NDQoNCmBgYHtyIGxtbV92dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAxDQoNCmxtbV9yZXN1bHRzJENvbG9yIDwtIHBhc3RlKCJOUyBvciBGQyA8ICIsZmNfdGhyZXNob2xkID0gMSkNCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJGBQcig+fHR8KWAgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpsbW1fcmVzdWx0cyRDb2xvcltsbW1fcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpsbW1fcmVzdWx0cyRDb2xvclthYnMobG1tX3Jlc3VsdHMkRXN0aW1hdGUpIDwgZmNfdGhyZXNob2xkXSA8LSBwYXN0ZSgiTlMgb3IgRkMgPCAiLGZjX3RocmVzaG9sZCA9IDEpDQpsbW1fcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IobG1tX3Jlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDEiLCAiUCA8IDAuMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KY291bnRlciA9IDENCnBsb3RzX2xtbSA8LSBsaXN0KCkNCmZvcihjIGluIHVuaXF1ZShsbW1fcmVzdWx0cyRTdWJzZXQpICkgew0KICBsbW1fcmVzdWx0c19zbGljZSA9IGxtbV9yZXN1bHRzW2xtbV9yZXN1bHRzJFN1YnNldCA9PSBjLF0NCg0KICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KICBsbW1fcmVzdWx0c19zbGljZSRpbnZlcnRfUCA8LSAoLWxvZzEwKGxtbV9yZXN1bHRzX3NsaWNlJGBQcig+fHR8KWApKSAqIHNpZ24obG1tX3Jlc3VsdHNfc2xpY2UkRXN0aW1hdGUpDQogIA0KICAjbG9vcCBoZXJlIG92ZXIgdGVzdGVkIGNvbmRpdGlvbnMgaWYgYXBwbGljYWJsZQ0KICB0b3BfZyA8LSBjKCkNCiAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICBsbW1fcmVzdWx0c19zbGljZVssICdHZW5lJ11bDQogICAgICBvcmRlcihsbW1fcmVzdWx0c19zbGljZVssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgIGxtbV9yZXN1bHRzX3NsaWNlWywgJ0dlbmUnXVsNCiAgICAgIG9yZGVyKGxtbV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KDQogIHRvcF9nIDwtIHVuaXF1ZSh0b3BfZykNCiAgDQogIGxtbV9yZXN1bHRzX3NsaWNlIDwtIGxtbV9yZXN1bHRzX3NsaWNlWywgLTEqbmNvbChsbW1fcmVzdWx0c19zbGljZSldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQogIA0KICAjIEZsaXAgQ29udHJhc3QNCiAgY29udHJhc3RfbGFiIDwtIGFzLmNoYXJhY3RlcihsbW1fcmVzdWx0c19zbGljZSRDb250cmFzdCkNCiAgY29udHJhc3RfbGFiIDwtIHN0cnNwbGl0KGNvbnRyYXN0X2xhYiwgIi0iKVtbMV1dDQogIGNvbnRyYXN0X2xhYiA8LSBwYXN0ZShjb250cmFzdF9sYWJbMl0sICItIiwgY29udHJhc3RfbGFiWzFdKQ0KICANCiAgIyBHcmFwaCByZXN1bHRzDQogIHBsb3RzX2xtbVtbY291bnRlcl1dIDwtIGdncGxvdChsbW1fcmVzdWx0c19zbGljZSwNCiAgICAgICAgICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSAtbG9nMTAoYFByKD58dHwpYCksDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSBDb2xvciwgbGFiZWwgPSBHZW5lKSkgKw0KICAgICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoZmNfdGhyZXNob2xkLCAtZmNfdGhyZXNob2xkKSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgICBsYWJzKA0KICAgICAgICAgICAgeCA9IHBhc3RlKGNvbnRyYXN0X2xhYiwgIiBsb2cyKEZDKSIpLA0KICAgICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDFgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGxtbV9yZXN1bHRzX3NsaWNlLCAgYFByKD58dHwpYCA8IDAuMDAxICYgYWJzKGxtbV9yZXN1bHRzX3NsaWNlJEVzdGltYXRlKSA+IGZjX3RocmVzaG9sZCAmIEdlbmUgJWluJSB0b3BfZyksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsc2l6ZT01LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgICAgZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgY291bnRlciA8LSBjb3VudGVyICsgMQ0KfQ0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzX2xtbSxuY29sPTIpDQpgYGAgICAgDQoNCmBgYHtyIHRhYmxlX29mX0xNTV9yZXN1bHRzIHJlZ2lvbn0NCiNzdWJzZXQobG1tX3Jlc3VsdHMsIEdlbmUgJWluJSBHT0kpDQpkYXRhdGFibGUobG1tX3Jlc3VsdHMsIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgICApLA0KICAgICAgICAgIGNhcHRpb24gPSAiREUgcmVzdWx0cyBmb3IgR2VuZXMgb2YgSW50ZXJlc3QgKD43NSUgQ1YpIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTMpDQpgYGANCg0KIyA3LjQgUGxvdHRpbmcgR2VuZXMgb2YgSW50ZXJlc3QNCg0KYGBge3IgcGxvdF9nZW5lX29mX2ludGVyZXN0LCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQ0KbXlfZ29pcyA8LXVuaXF1ZShzdWJzZXQobG1tX3Jlc3VsdHMsIGBGRFJgIDwgMC4wMDEpJEdlbmUpDQp0bXBfdGJsPC1zdWJzZXQobG1tX3Jlc3VsdHMsIEdlbmUgJWluJSBteV9nb2lzKQ0KDQpteV9nb2lzIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUodG1wX3RibCRDb250cmFzdCkpIHsgIyBnZW5lIG9mIGludGVyZXN0IGZvciBhbGwgY29udHJhc3RzDQojZm9yIChjb250cmFzdCBpbiBjKCJKdXhfR2xvIC0gUHJvX1R1YiIpKSB7DQogIGluZCA8LSB0bXBfdGJsJENvbnRyYXN0ID09IGNvbnRyYXN0DQogIGdvaSA8LSB0bXBfdGJsW2luZCwgIkdlbmUiXVtvcmRlcih0bXBfdGJsW2luZCwgIkVzdGltYXRlIl0sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMTozXV0NCiAgbXlfZ29pcyA8LSBjKG15X2dvaXMsIGdvaSkNCiAgZ29pIDwtIHRtcF90YmxbaW5kLCAiR2VuZSJdW29yZGVyKHRtcF90YmxbaW5kLCAiRXN0aW1hdGUiXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6M11dDQogIG15X2dvaXMgPC0gYyhteV9nb2lzLCBnb2kpDQp9DQoNCmlmKG5yb3codG1wX3RibCkgPiAxKSB7IA0KICBkYXRhdGFibGUodG1wX3RibCxyb3duYW1lcyA9IEZBTFNFLGNhcHRpb24gPSAiREUgcmVzdWx0cyBmb3IgR2VuZXMgb2YgSW50ZXJlc3QgIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTMpDQogDQpmb3IgKG15X2dvaSBpbiBteV9nb2lzKSB7DQojIHNob3cgZXhwcmVzc2lvbiBmb3IgYSBzaW5nbGUgdGFyZ2V0DQogIGE8LWdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICAgeSA9IGFzLm51bWVyaWMoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtteV9nb2ksIF0sIGVsdCA9ICJxX25vcm0iKSkpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9IHBhc3RlKG15X2dvaSwiRXhwcmVzc2lvbiIpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICAjZmFjZXRfd3JhcCh+QU5OMywgbnJvdz0xKSArIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCiAgcHJpbnQoYSkNCn0NCn1lbHNlew0KICBwcmludCgiTm8gc2lnbmlmaWNhbnQgbE1NIHJlc3VsdHMgdG8gcGxvdCIpDQp9DQpgYGANCg0KIyA3LjUgSGVhdG1hcCBvZiBTaWduaWZpY2FudCBHZW5lcw0KDQpJbiBhZGRpdGlvbiB0byBnZW5lcmF0aW5nIGluZGl2aWR1YWwgZ2VuZSBib3ggcGxvdHMgb3Igdm9sY2FubyBwbG90cywgd2UNCmNhbiBhZ2FpbiBjcmVhdGUgYSBoZWF0bWFwIGZyb20gb3VyIGRhdGEuIFRoaXMgdGltZSByYXRoZXIgdGhhbg0KdXRpbGl6aW5nIENWIHRvIHNlbGVjdCBnZW5lcywgd2UgY2FuIHVzZSB0aGUgUC12YWx1ZSBvciBGRFIgdmFsdWVzIHRvDQpzZWxlY3QgZ2VuZXMuIEhlcmUsIHdlIHBsb3QgYWxsIGdlbmVzIHdpdGggYW4gRkRSIFw8IDAuMDAxLg0KDQpgYGB7ciBoZWF0bWFwX3NpZ19nZW5lcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQpteV9nb2lzIDwtdW5pcXVlKHN1YnNldChsbW1fcmVzdWx0cywgYEZEUmAgPCAwLjAwMSkkR2VuZSkgICAjIDEwMCB0byBwcmV2ZW50IGxvbmcgcnVudGltZQ0KDQppZihsZW5ndGgobXlfZ29pcyk8Mikgew0KICBwcmludCgiTm8gc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBzaG93IikNCiANCn1lbHNlew0KDQpwaGVhdG1hcChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbbXlfZ29pcywgXSwgZWx0ID0gInFfbm9ybSIpKSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQp9DQpgYGANCg0KIyA4IFBhdGh3YXkgQW5hbHlzaXMNCg0KUGF0aHdheSBhbmFseXNpcyBlbmFibGVzIGV4cGxvcmF0aW9uIG9mIGRpZmZlcmVudCBhZ2dyZWdhdGUgZ2VuZSBzZXRzDQpmb3Igb3VyIGV4cGVyaW1lbnRhbCBxdWVzdGlvbnMuIEVhY2ggaW5kaXZpZHVhbCBST0kvQU9JIHNlZ21lbnQgaXMNCnNjb3JlZCBmb3IgZXZlcnkgcGF0aHdheSBvZiBpbnRlcmVzdCwgd2hpY2ggd2UgY2FuIHRoZW4gdXNlIHRvDQppbnZlc3RpZ2F0ZSBiaW9sb2dpY2FsIGRpZmZlcmVuY2VzLiBXZSB3aWxsIHBlcmZvcm0gYW5hbG9nb3VzIGFuYWx5c2VzDQphcyB0aG9zZSBvdXRsaW5lZCBpbiB0aGUgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYW5kIFNwYXRpYWwNCkRlY29udm9sdXRpb24gc2VjdGlvbnMgb2YgdGhlIHJlcG9ydCBmb3IgZ2VuZSBzZXQgZW5yaWNobWVudC4NCg0KIyA4LjEgU2NvcmluZyBHZW5lIFNldHMNCg0KUGF0aHdheXMgYW5kIGdlbmUgc2V0cyB3ZXJlIGRlZmluZWQgZnJvbSB0aGUgS2VnZyBCcml0ZSBkYXRhYmFzZS4gV2UgdXNlDQphbiBSIHNvZnR3YXJlIHBhY2thZ2UgY2FsbGVkIEdlbmUgU2V0IFZhcmlhdGlvbiBBbmFseXNpcyB0byBzY29yZSBlYWNoDQpzZWdtZW50IHdpdGhpbiBvdXIgc3R1ZHkuIHNlZQ0KPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tc2lnZGJyL3ZpZ25ldHRlcy9tc2lnZGJyLWludHJvLmh0bWw+DQpmb3Igb3B0aW9ucyBvbiBjb2xsZWN0aW9ucy4gV2UgdXNlIHRoZSBLRUdHIGFuZCBSRUFDVE9NRS4NCg0KYGBge3IgYnVpbGRfZ2VuZXNldHN9DQojZ2MoKQ0KaF9nZW5lX3NldHMgPSByYmluZChtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBzdWJjYXRlZ29yeSA9ICJDUDpLRUdHIiksDQogICAgICAgICAgICAgICAgICAgIG1zaWdkYnIoc3BlY2llcyA9ICJodW1hbiIsIHN1YmNhdGVnb3J5ID0gIkNQOlJFQUNUT01FIikpDQojbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgc3ViY2F0ZWdvcnkgPSAiQ1A6QklPQ0FSVEEiKQ0KDQptc2lnZGJyX2xpc3QgPSBzcGxpdCh4ID0gaF9nZW5lX3NldHMkZ2VuZV9zeW1ib2wsIGYgPSBoX2dlbmVfc2V0cyRnc19uYW1lKQ0KDQojIHByZXBhcmUgZGYgZm9yIGFjY3VyYXRlIG1lcmdpbmcgYmFjayBnZW5lcyBsYXRlcg0KcHdfZ2VuZV9kZjwtZGF0YS5mcmFtZShQYXRod2F5ID0gbmFtZXMobXNpZ2Ricl9saXN0KSkNCnB3X2dlbmVfZGYkZ2VuZXM8LW1zaWdkYnJfbGlzdA0KYGBgDQoNCmBgYHtyIHJ1bl9nc3ZhfQ0Kc3Nnc2VhX3Jlc3VsdHMgPC0gR1NWQTo6Z3N2YShleHByID0gYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnc2V0LmlkeC5saXN0ID0gbXNpZ2Ricl9saXN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzc2dzZWEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zeiA9IDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LnN6ID0gNTAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkNCmdlbmVTZXRPYmogPC0NCiAgTmFub1N0cmluZ0dlb014U2V0KGFzc2F5RGF0YSA9IHNzZ3NlYV9yZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhID0gQW5ub3RhdGVkRGF0YUZyYW1lKHBEYXRhKHRhcmdldF9EYXRhKSksDQogICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGEgPSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZVR5cGUgPSAiR2VuZVNldCIsDQogICAgICAgICAgICAgICAgICAgICBjaGVjayA9IEZBTFNFKQ0KYGBgDQoNCiMgOC4yIERpZmZlcmVudGFsIGFuYWx5c2lzIG9mIHBhdGh3YXlzDQoNCmBgYHtyIExNTV9vZl9wYXRod2F5X2FuYWxpc2lzIHJlZ2lvbn0NCiMgIyBjb252ZXJ0IHRlc3QgdmFyaWFibGVzIHRvIGZhY3RvcnMNCnBEYXRhKHRhcmdldF9EYXRhKSR0ZXN0Q2xhc3MgPC0gZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4xLCB1bmlxdWUodGFyZ2V0X0RhdGEkQU5OMSkpDQpwRGF0YShnZW5lU2V0T2JqKVtbInNsaWRlIl1dPC1mYWN0b3IocERhdGEoZ2VuZVNldE9iailbWyJzbGlkZV9uYW1lIl1dKQ0KI3BEYXRhKHRhcmdldF9EYXRhKSR0ZXN0UmVnaW9uPC1mYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjIsIHVuaXF1ZShjb3VudF9tYXQkQU5OMykpDQoNCmxtbV9zc2dzZWFfcmVzdWx0cyA8LSBjKCkNCmZvciAocmVnaW9uIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMykpIHsNCiAgaW5kIDwtIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zID09IHJlZ2lvbg0KICBtaXhlZE91dG1jIDwtDQogICAgbWl4ZWRNb2RlbERFKGdlbmVTZXRPYmpbLCBpbmRdLA0KICAgICAgICAgICAgICAgICBlbHQgPSAiZXhwcnMiLA0KICAgICAgICAgICAgICAgICBtb2RlbEZvcm11bGEgPSB+IHRlc3RDbGFzcyArICgxIHwgc2xpZGUpLA0KICAgICAgICAgICAgICAgICBncm91cFZhciA9ICJ0ZXN0Q2xhc3MiLA0KICAgICAgICAgICAgICAgICBuQ29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSwNCiAgICAgICAgICAgICAgICAgbXVsdGlDb3JlID0gVFJVRSkNCg0KICAjIGZvcm1hdCByZXN1bHRzIGFzIGRhdGEuZnJhbWUNCiAgcl9zc2dzZWFfdGVzdCA8LSBkby5jYWxsKHJiaW5kLCBtaXhlZE91dG1jWyJsc21lYW5zIiwgXSkNCiAgc3Nnc2VhX3Rlc3RzIDwtIHJvd25hbWVzKHJfc3Nnc2VhX3Rlc3QpDQogIHJfc3Nnc2VhX3Rlc3QgPC0gYXMuZGF0YS5mcmFtZShyX3NzZ3NlYV90ZXN0KQ0KICByX3NzZ3NlYV90ZXN0JENvbnRyYXN0IDwtIHNzZ3NlYV90ZXN0cw0KICAjcl9zc2dzZWFfdGVzdCRHZW5lcyA8LSBtc2lnZGJyX2xpc3Qgc2VlbXMgdW5yZWxpYWJsZSBhcyBnc3ZhIG9taXRzIHBhdGh3YXlzIGlmIGdlbmVzIGFyZSBub3QgaW4gZGF0YS4uDQoNCiAgIyB1c2UgbGFwcGx5IGluIGNhc2UgeW91IGhhdmUgbXVsdGlwbGUgbGV2ZWxzIG9mIHlvdXIgdGVzdCBmYWN0b3IgdG8NCiAgIyBjb3JyZWN0bHkgYXNzb2NpYXRlIGdlbmUgbmFtZSB3aXRoIGl0J3Mgcm93IGluIHRoZSByZXN1bHRzIHRhYmxlDQogIHJfc3Nnc2VhX3Rlc3QkUGF0aHdheSA8LQ0KICAgIHVubGlzdChsYXBwbHkoY29sbmFtZXMobWl4ZWRPdXRtYyksDQogICAgICAgICAgICAgICAgICByZXAsIG5yb3cobWl4ZWRPdXRtY1sibHNtZWFucyIsIF1bWzFdXSkpKQ0KDQogICNyX3NzZ3NlYV90ZXN0JFN1YnNldCA8LSBzdGF0dXMNCiAgcl9zc2dzZWFfdGVzdCRGRFIgPC0gcC5hZGp1c3Qocl9zc2dzZWFfdGVzdCRgUHIoPnx0fClgLCBtZXRob2QgPSAiZmRyIikNCiAgcl9zc2dzZWFfdGVzdCRTdWJzZXQgPC0gcmVnaW9uDQogIHJfc3Nnc2VhX3Rlc3QgPC0gcl9zc2dzZWFfdGVzdFssIGMoIlBhdGh3YXkiLCAiU3Vic2V0IiwgIkNvbnRyYXN0IiwgIkVzdGltYXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUHIoPnx0fCkiLCAiRkRSIildDQogIGxtbV9zc2dzZWFfcmVzdWx0cyA8LSByYmluZChsbW1fc3Nnc2VhX3Jlc3VsdHMsIHJfc3Nnc2VhX3Rlc3QpDQoNCiAgfQ0KICBsbW1fc3Nnc2VhX3Jlc3VsdHMgPC0gbWVyZ2UobG1tX3NzZ3NlYV9yZXN1bHRzLCBwd19nZW5lX2RmKQ0KYGBgDQoNCiMgOC4yLjEgVGFibGUgb2YgRGlmZmVyZW50YWwgYW5hbHlzaXMgb2YgcGF0aHdheXMNCg0KYGBge3IgdGFibGVfb2ZfTE1NX3BhdGh3YXlfcmVzdWx0cyByZWdpb24sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KZGF0YXRhYmxlKHN1YnNldChsbW1fc3Nnc2VhX3Jlc3VsdHMpLCByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gMTAsDQogICAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBQYXRod2F5cyIsZmlsdGVyPSd0b3AnKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJFc3RpbWF0ZSIsIlByKD58dHwpIiwiRkRSIiksIGRpZ2l0cz01KQ0KYGBgDQoNCiMgOC4zIFZ1bGNhbm9wbG90IG9mIExNTV9QYXRod2F5cw0KDQpgYGB7ciBsbW1fcHdfdnVsY2FubywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIENhdGVnb3JpemUgUmVzdWx0cyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KZmNfdGhyZXNob2xkID0gMC4zDQoNCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjMiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJGBQcig+fHR8KWAgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yW2xtbV9zc2dzZWFfcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yW2FicyhsbW1fc3Nnc2VhX3Jlc3VsdHMkRXN0aW1hdGUpIDwgZmNfdGhyZXNob2xkXSA8LSAiTlMgb3IgRkMgPCAwLjMiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKGxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC4zIiwgIlAgPCAwLjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KIyBwaWNrIHRvcCBwdyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KIyBvcmRlciBwdyBmb3IgY29udmVuaWVuY2U6DQpsbW1fc3Nnc2VhX3Jlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChsbW1fc3Nnc2VhX3Jlc3VsdHMkYFByKD58dHwpYCkpICogc2lnbihsbW1fc3Nnc2VhX3Jlc3VsdHMkRXN0aW1hdGUpDQp0b3Bfc3Nnc2VhX2cgPC0gYygpDQoNCiNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQoNCmZvcihjIGluIHVuaXF1ZShsbW1fc3Nnc2VhX3Jlc3VsdHMkU3Vic2V0KSApIHsNCiAgbG1tX3Jlc3VsdHNfc2xpY2UgPSBsbW1fc3Nnc2VhX3Jlc3VsdHNbbG1tX3NzZ3NlYV9yZXN1bHRzJFN1YnNldCA9PSBjLF0NCiB0b3Bfc3Nnc2VhX2cgPC0gYyh0b3Bfc3Nnc2VhX2csDQogICAgICAgICBsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnUGF0aHdheSddWw0KICAgICAgICAgICAgICAgb3JkZXIobG1tX3NzZ3NlYV9yZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgICAgICBsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnUGF0aHdheSddWw0KICAgICAgICAgICAgICAgb3JkZXIobG1tX3NzZ3NlYV9yZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KfQ0KIA0KdG9wX3NzZ3NlYV9nIDwtIHVuaXF1ZSh0b3Bfc3Nnc2VhX2cpDQpsbW1fc3Nnc2VhX3Jlc3VsdHMgPC0gbG1tX3NzZ3NlYV9yZXN1bHRzWywgLTEqbmNvbChsbW1fc3Nnc2VhX3Jlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQojbG1tX3NzZ3NlYV9yZXN1bHRzX3NsaWNlIDwtIGxtbV9zc2dzZWFfcmVzdWx0c19zbGljZVtsbW1fc3Nnc2VhX3Jlc3VsdHNfc2xpY2UkRkRSIDwgMSxdDQoNCiMgR3JhcGggcmVzdWx0cw0KZ2dwbG90KGxtbV9zc2dzZWFfcmVzdWx0cywNCiAgICAgICBhZXMoeCA9IEVzdGltYXRlLCB5ID0gLWxvZzEwKGBQcig+fHR8KWApLA0KICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IFBhdGh3YXkpKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLjIsIC0wLjIpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGxhYnMoeCA9IHBhc3RlKGxtbV9yZXN1bHRzX3NsaWNlJENvbnRyYXN0LCAibG9nMihGQykiKSwNCiAgICAgICAgIHkgPSAiU2lnbmlmaWNhbmNlLCAtbG9nMTAoUCkiLA0KICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEZEUiA8IDAuMDVgID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjVgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGxtbV9zc2dzZWFfcmVzdWx0cywgQ29sb3IgPT0gIkZEUiA8IDAuMDUiIHwgQ29sb3IgPT0gIkZEUiA8IDAuMDAxIiksDQogICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTMsDQogICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikNCg0KIyAgICArZmFjZXRfd3JhcCh+U3Vic2V0LCBzY2FsZXMgPSAiZnJlZV95IikpDQpgYGANCg0KIyA4LjQgaGVhdG1hcCBvZiBwYXRod2F5cw0KDQpgYGB7ciBwd19oZWF0bWFwLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCiAgYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBgUHIoPnx0fClgIDwgMC4wMDEpJFBhdGh3YXkNCiAgYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBGRFIgPCAwLjAwMSApJFBhdGh3YXkNCiAgI2FjdGl2ZV9wdzwtZmlsdGVyKGxtbV9zc2dzZWFfcmVzdWx0cywgQ29sb3IgPT0gIkZEUiA8IDAuMDAxIikkUGF0aHdheQ0KICANCiAgDQogIGFjdGl2ZV9wdzwtdG9wX3NzZ3NlYV9nDQogIA0KICBpZiAobGVuZ3RoKGFjdGl2ZV9wdyk+MSkgew0KICANCiAgICBwcmludCgiZ28iKQ0KICAgIA0KICBwd19tYXRyaXg8LWFzc2F5RGF0YUVsZW1lbnQoZ2VuZVNldE9iaiwgZWx0ID0gImV4cHJzIikNCg0KICBwaGVhdG1hcChwd19tYXRyaXhbYWN0aXZlX3B3LF0sDQogICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTAsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiZXVjbGlkZWFuIiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMiwgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgYnJlYWtzID0gc2VxKC0zLCAzLCAwLjA1KSwNCiAgICAgICAgICNjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygicHVycGxlMyIsICJibGFjayIsICJ5ZWxsb3cyIikpKDEyMCksDQogICAgICAgICBtYWluID0gIkhlYXRtYXAgb2Ygc2VsZWN0ZWQgUGF0aHdheXMiLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KICANCiAgfWVsc2V7DQogICAgcHJpbnQoIk5vIHNpZ25pZmljYW50IHJlc3VsdHMgdG8gZGlzcGxheSIpDQogIH0NCmBgYA0KDQojIDguNSBmZ3NlYSBwYXRod2F5IGFuYWx5c2lzDQoNCkFub3RoZXIgb3B0aW9uIGZvciBwYXRod2F5IGFuYWx5c2lzIGlzIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgY2FsbGVkIEZhc3QgR2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpcy4gSXQgaXMgYSBwcmUtcmFua2VkIG1ldGhvZCB0aGF0IHVzZXMgdGhlIExNTSByZXN1bHRzIGNvbWJpbmVkIHdpdGggdGhlIHNhbWUgcGF0aHdheSBsaXN0IGZyb20gbXNpZ2RiciBhcyBHU1ZBLg0KDQpgYGB7ciBmZ3NlYX0NCmZnc2VhX3Jlc3VsdHNfYWxsIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUobG1tX3Jlc3VsdHMkQ29udHJhc3QpKSB7DQogICAgDQogICAgcmFua3MgPC0gbG1tX3Jlc3VsdHMkRXN0aW1hdGVbbG1tX3Jlc3VsdHMkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgbmFtZXMocmFua3MpIDwtIGxtbV9yZXN1bHRzJEdlbmVbbG1tX3Jlc3VsdHMkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgDQogICAgZmdzZWFfcmVzdWx0cyA8LSBmZ3NlYShtc2lnZGJyX2xpc3QsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHJhbmtzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBlcHMgPSAwLjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluU2l6ZT0xNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbWF4U2l6ZSA9IDUwMCkNCiAgICBmZ3NlYV9yZXN1bHRzJENvbnRyYXN0IDwtIGNvbnRyYXN0DQogICAgZmdzZWFfcmVzdWx0c19hbGwgPC0gcmJpbmQoZmdzZWFfcmVzdWx0c19hbGwsIGZnc2VhX3Jlc3VsdHMpDQogICAgDQp9DQoNCmZnc2VhX3JlYWN0b21lIDwtIGZnc2VhX3Jlc3VsdHNfYWxsW2dyZXAoIlJFQUNUT01FIiwgcGF0aHdheSksXQ0KZmdzZWFfcmVhY3RvbWUkcGF0aHdheSA8LSBnc3ViKCJSRUFDVE9NRV8iLCAiIiwgZmdzZWFfcmVhY3RvbWUkcGF0aHdheSkNCmZnc2VhX3JlYWN0b21lJHBhdGh3YXkgPC0gZ3N1YigiXyIsICIgIiwgZmdzZWFfcmVhY3RvbWUkcGF0aHdheSkNCmBgYA0KDQpgYGB7ciBmZ3NlYV9wd192dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAwLjMNCg0KDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjMiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwdmFsIDwgMC4wNV0gPC0gIlAgPCAwLjA1Ig0KZmdzZWFfcmVzdWx0c19hbGwkQ29sb3JbZmdzZWFfcmVzdWx0c19hbGwkcGFkaiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KZmdzZWFfcmVzdWx0c19hbGwkQ29sb3JbZmdzZWFfcmVzdWx0c19hbGwkcGFkaiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvclthYnMoZmdzZWFfcmVzdWx0c19hbGwkRVMpIDwgZmNfdGhyZXNob2xkXSA8LSAiTlMgb3IgRkMgPCAwLjMiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciA8LSBmYWN0b3IoZmdzZWFfcmVzdWx0c19hbGwkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuMyIsICJQIDwgMC4wNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCnBsdCA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQ0KY291bnRlciA9IDENCmZvcihjIGluIHVuaXF1ZShmZ3NlYV9yZXN1bHRzX2FsbCRDb250cmFzdCkgKSB7DQogIGZnc2VhX3Jlc3VsdHNfc2xpY2UgPSBmZ3NlYV9yZXN1bHRzX2FsbFtmZ3NlYV9yZXN1bHRzX2FsbCRDb250cmFzdCA9PSBjLF0NCg0KICAjICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgIyAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCiAgZmdzZWFfcmVzdWx0c19zbGljZSRpbnZlcnRfUCA8LSAoLWxvZzEwKGZnc2VhX3Jlc3VsdHNfc2xpY2UkcHZhbCkpICogc2lnbihmZ3NlYV9yZXN1bHRzX3NsaWNlJEVTKQ0KICANCiAgIyAjbG9vcCBoZXJlIG92ZXIgdGVzdGVkIGNvbmRpdGlvbnMgaWYgYXBwbGljYWJsZQ0KICAjdG9wX2Znc2VhX2cgPC0gYygpDQogIHRvcF9mZ3NlYV9nIDwtIGMoZmdzZWFfcmVzdWx0c19zbGljZVssICdwYXRod2F5J11bDQogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAncGF0aHdheSddWw0KICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXIoZmdzZWFfcmVzdWx0c19zbGljZVssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgIA0KICAjIGZvciAoY2VsbHR5cGUgaW4gdW5pcXVlKGxtbV9yZXN1bHRzJFN1YnNldCkpIHsNCiAgIyAgICAgIHRvcF9mZ3NlYV9nIDwtIGModG9wX2Znc2VhX2csDQogICMgICAgICAgICAgICAgICAgICBmZ3NlYV9yZXN1bHRzX3NsaWNlW2Znc2VhX3Jlc3VsdHNfc2xpY2UkU3Vic2V0PT1jZWxsdHlwZSwgJ3BhdGh3YXknXVsNCiAgIyAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjIwXV0sDQogICMgICAgICAgICAgICAgICAgICBmZ3NlYV9yZXN1bHRzX3NsaWNlW2Znc2VhX3Jlc3VsdHNfc2xpY2UkU3Vic2V0PT1jZWxsdHlwZSwgJ3BhdGh3YXknXVsNCiAgIyAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToyMF1dKQ0KICAjIH0NCiAgDQogIHRvcF9mZ3NlYV9nIDwtIHVuaXF1ZSh0b3BfZmdzZWFfZykNCiAgI2Znc2VhX3Jlc3VsdHNfc2xpY2UgPC0gZmdzZWFfcmVzdWx0c19zbGljZVssIC0xKm5jb2woZmdzZWFfcmVzdWx0c19zbGljZSldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQogIA0KICAjIEdyYXBoIHJlc3VsdHMNCiAgZHlucGxvdCA8LSBnZ3Bsb3QoZmdzZWFfcmVzdWx0c19zbGljZSwNCiAgICAgICAgIGFlcyh4ID0gTkVTLCB5ID0gLWxvZzEwKHB2YWwpLA0KICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gcGF0aHdheSkpICsNCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoZmNfdGhyZXNob2xkLC1mY190aHJlc2hvbGQpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgIGxhYnMoeCA9IHBhc3RlKGMsICIgRkMiKSwNCiAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjNgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjA1KSkpICsNCiAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KGZnc2VhX3Jlc3VsdHNfc2xpY2UsIENvbG9yID09ICJQIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSwNCiAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsc2l6ZT01LA0KICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsNCiAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAjKw0KICAgICAgI2ZhY2V0X3dyYXAoflN1YnNldCwgc2NhbGVzID0gImZyZWVfeSIpDQogICANCiAgcGx0W1tjb3VudGVyXV0gPC0gYXNfd2lkZ2V0KGdncGxvdGx5KGR5bnBsb3QpKQ0KICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxDQogICNkYXRhdGFibGUoc3Vic2V0KHJlc3VsdHMsIEdlbmUgJWluJSBHT0kpLCByb3duYW1lcz1GQUxTRSxjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpKQ0KfQ0KcGx0DQpgYGANCg0KIyA4LjYgVmlzdWFsaXplIEtFR0cgcGF0aHdheQ0KDQpHZXQgcGF0aHdheSB2aXN1YWxpemF0aW9uIGZyb20gS0VHRy4gY2x1c3RlclByb2ZpbGVyIGdyYWJzIHRoZSBLRUdHIHBhdGh3YXkgSURzIGFuZCBjb21iaW5lZCB3aXRoIHBhdGh2aWV3IGl0IGNhbiBkb3dubG9hZCBhIHBhdGh3YXkgaW1hZ2UgZnJvbSB0aGUgS0VHRyBkYXRhYmFzZS4NClRoZSBMTU0gcmVzdWx0cyBhcmUgZ2l2ZW4gd2l0aCB0aGUgcGF0aHdheSBpZCB0byB2aXN1YWxpemUgdXAgYW5kIGRvd24gcmVndWxhdGVkIGdlbmVzLg0KDQpgYGB7cn0NCmdldF93cF9nbXRmaWxlIDwtIGZ1bmN0aW9uKCkgew0KICAgIHdwdXJsIDwtICdodHRwczovL3dpa2lwYXRod2F5cy1kYXRhLndtY2xvdWQub3JnL2N1cnJlbnQvZ210LycNCiAgICB4IDwtIHJlYWRMaW5lcyh3cHVybCkNCiAgICB5IDwtIHhbZ3JlcCgnXFwuZ210Jyx4KV0NCiAgICBzdWIoIi4qKHdpa2lwYXRod2F5cy0uKlxcLmdtdCkuKiIsICJcXDEiLCAgeVtncmVwKCdGaWxlJywgeSldKQ0KfQ0KDQpnZXRfd3BfZGF0YSA8LSBmdW5jdGlvbihvcmdhbmlzbSkgew0KICAgIG9yZ2FuaXNtIDwtIHN1YigiICIsICJfIiwgb3JnYW5pc20pDQogICAgZ210ZmlsZSA8LSBnZXRfd3BfZ210ZmlsZSgpDQogICAgd3B1cmwgPC0gJ2h0dHBzOi8vd2lraXBhdGh3YXlzLWRhdGEud21jbG91ZC5vcmcvY3VycmVudC9nbXQvJw0KICAgIHVybCA8LSBwYXN0ZTAod3B1cmwsDQogICAgICAgICAgICAgICAgICBnbXRmaWxlW2dyZXAob3JnYW5pc20sIGdtdGZpbGUpXSkNCiAgICBmIDwtIHRlbXBmaWxlKGZpbGVleHQgPSAiLmdtdCIpDQogICAgZGwgPC0gbXlkb3dubG9hZCh1cmwsIGRlc3RmaWxlID0gZikNCiAgICBpZiAoaXMubnVsbChmKSkgew0KICAgICAgICBtZXNzYWdlKCJmYWlsIHRvIGRvd25sb2FkIHdpa2lQYXRod2F5cyBkYXRhLi4uIikNCiAgICAgICAgcmV0dXJuKE5VTEwpDQogICAgfQ0KICAgIHJlYWQuZ210LndwKGYpDQp9DQpgYGANCg0KDQpgYGB7ciBnZXQgb25saW5lIGF2YWlsYWJsZSBwYXRod2F5cywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIGVucmljaEtFR0cgZ3JhYiBvbmxpbmUgcGF0aHdheXMNCmdlbmUgPC0gIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCmtrIDwtIGVucmljaEtFR0coZ2VuZSAgICAgICAgID0gZ2VuZSwNCiAgICAgICAgICAgICAgICAgb3JnYW5pc20gICAgID0gJ2hzYScsDQogICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMSkNCg0KZW5yaWNoS0VHR19yZXN1bHRzIDwtIGtrQHJlc3VsdA0KDQojIE1hdGNoIGNvbHVtbiB2YWx1ZXMgZm9yIGxpbmsNCiMgZnNnZWEgcnVuDQpmZ3NlYV9yZXN1bHRzJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGZnc2VhX3Jlc3VsdHMkcGF0aHdheSkNCmZnc2VhX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gZ3N1YigiXyIsICIgIiwgZmdzZWFfcmVzdWx0cyREZXNjcmlwdGlvbikNCiMgc3NnZWEgcnVuDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJFBhdGh3YXkpDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIl8iLCAiICIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uKQ0KDQplbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gdG91cHBlcihlbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24pDQoNCiMgQ3JlYXRlIGRmIGZvciBtYXRjaGluZyBLRUdHIHJlc3VsdHMNCktFR0dJRHMgPC0gZmdzZWFfcmVzdWx0cyNbZmdzZWFfcmVzdWx0cyRDb250cmFzdCA9PSAiUHJvX1R1YiAtIERpc19UdWIiXQ0KS0VHR0lEcyA8LSBLRUdHSURzICU+JSBpbm5lcl9qb2luKGVucmljaEtFR0dfcmVzdWx0cywgYnkgPSAnRGVzY3JpcHRpb24nKSAlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCg0KIyBDcmVhdGUgZ2VuZUxpc3Qgd2l0aCBERSByZXN1bHRzIGZvciB2aXN1YWxpemF0aW9uIHBhdGh3YXkgZ2VuZXMgDQpnZW5lbGlzdCA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZQ0KbmFtZXMoZ2VuZWxpc3QpIDwtIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCg0KIyBDaG9vc2UgbWFudWFsIG9yIGhpZ2hlc3Qgc2lnbmlmaWNhbnQgcGF0aHdheQ0KcGF0aHdheV9uYW1lIDwtICJJTlNVTElOIFNJR05BTElORyBQQVRIV0FZIiAjIG1hbnVhbCBwYXRod2F5IHF1ZXJ5DQpwYXRod2F5X25hbWUgPC0gdG91cHBlcihwYXRod2F5X25hbWUpDQppZiAocGF0aHdheV9uYW1lID09ICIiKSB7DQogIHBhdGh3YXlfbmFtZSA8LSBLRUdHSURzW29yZGVyKEtFR0dJRHMkcGFkaiwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXVsxXSREZXNjcmlwdGlvbg0KfQ0KcGF0aHdheWlkIDwtIEtFR0dJRHMkSURbS0VHR0lEcyREZXNjcmlwdGlvbiA9PSBwYXRod2F5X25hbWVdDQoNCiMgR3JhYiBwYXRod2F5IGltYWdlIHdpdGggZ2VuZSBpbmZvLCBzYXZlIGFuZCBwbG90DQp2aWV3UGF0aCA8LSBwYXRodmlldyhnZW5lLmRhdGEgID0gZ2VuZWxpc3QsDQogICAgICAgICAgICAgICAgICAgICBwYXRod2F5LmlkID0gcGF0aHdheWlkLA0KICAgICAgICAgICAgICAgICAgICAgc3BlY2llcyAgICA9ICJoc2EiLA0KICAgICAgICAgICAgICAgICAgICAgb3V0LnN1ZmZpeCA9ICJnZW5lc2luZm8iLCBrZWdnLm5hdGl2ZSA9IFQsIHNhbWUubGF5ZXIgPSBGKQ0KaW1nIDwtIHJlYWRQTkcocGFzdGUocGF0aHdheWlkLCAiLmdlbmVzaW5mby5wbmciLCBzZXAgPSAiIikpDQpncmlkOjpncmlkLnJhc3RlcihpbWcpDQpgYGANCg0KIyMgQmFycGxvdCBwYXRod2F5cw0KDQpDbGVhciB2aXp1YWxpemF0aW9uIG9mIHRoZSB0b3AgMTUgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIGV4cHJlc3NlZCBSZWFjdG9tZSBwYXRod2F5cy4gUGF0aHdheXMgd2l0aCBhIGFkanVzdGVkIHAtdmFsdWUgb2YgMC4wNSBvciBoaWdoZXIgd2lsbCBiZSBwcmVzZW50ZWQgYXMgcmVkIA0KYW5kIGJlbG93IDAuMDUgYXMgZ3JlZW4uDQoNCmBgYHtyIGhvcml6b250YWwgcGF0aHdheSBiYXIgcGxvdCwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQp0b3BfYWxsIDwtIGMoKQ0KZm9yIChjb250cmFzdCBpbiB1bmlxdWUoZmdzZWFfcmVhY3RvbWUkQ29udHJhc3QpKSB7DQogICAgIyBhY3RpdmVfZ3JvdXAxIDwtIGNvbnRyYXN0X2xpc3RbY29udHJhc3RdW1sxXV1bWzFdXQ0KICAgICMgYWN0aXZlX2dyb3VwMiA8LSBjb250cmFzdF9saXN0W2NvbnRyYXN0XVtbMV1dW1syXV0NCiAgICAjIGluZCA8LSBwRGF0YSh0YXJnZXRfRGF0YSlbcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIgPT0gYWN0aXZlX2dyb3VwMSB8IHBEYXRhKHRhcmdldF9EYXRhKSRBTk4yID09IGFjdGl2ZV9ncm91cDJdDQogICAgdG9wIDwtIGZnc2VhX3JlYWN0b21lW2Znc2VhX3JlYWN0b21lJENvbnRyYXN0ID09IGNvbnRyYXN0XQ0KICAgIHRvcHBvcyA8LSB0b3Bbb3JkZXIodG9wJE5FUywgZGVjcmVhc2luZyA9IFRSVUUpXVswOjE1XQ0KICAgIHRvcG5lZyA8LSB0b3Bbb3JkZXIodG9wJE5FUywgZGVjcmVhc2luZyA9IEZBTFNFKV1bMDoxNV0NCiAgICB0b3AgPC0gcmJpbmQodG9wcG9zLCB0b3BuZWcpDQogICAgDQogICAgdG9wJENvbG9yW3RvcCRwYWRqIDwgMC4wNV0gPC0gInBhZGogPCAwLjA1Ig0KICAgIHRvcCRDb2xvclt0b3AkcGFkaiA9PSAwLjA1IHwgdG9wJHBhZGogPiAwLjA1XSA8LSAicGFkaiA+IDAuMDUiDQogICAgdG9wX2FsbCA8LSByYmluZCh0b3BfYWxsLCB0b3ApDQogICAgYmFycGxvdCA8LSBnZ3Bsb3QodG9wLGFlcyh4PXJlb3JkZXIocGF0aHdheSwgTkVTKSx5PU5FUyxmaWxsPUNvbG9yKSkgKyANCiAgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMDApKSArIA0KICAgICAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMkVDQzcxIiwgIiNFNzRDM0MiKSkgKw0KICAgICAgZ2d0aXRsZShwYXN0ZSgiVG9wIDE1ICsgYW5kIC0gTkVTIHJlYWN0b21lIHBhdGh3YXlzIGZyb20iLCBjb250cmFzdCkpICsgDQogICAgICB4bGFiKCJwYXRod2F5IikgKyB5bGFiKCJOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUiKSArDQogICAgICBjb29yZF9mbGlwKCkgKyANCiAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgKw0KICAgICAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkNCiAgICAgICNmYWNldF93cmFwKH5Db250cmFzdCwgc2NhbGVzID0gImZyZWVfeSIpDQogICAgcHJpbnQoYmFycGxvdCkNCn0NCmBgYA0KDQojIDkgU3BhdGlhbCBEZWNvbnZvbHV0aW9uDQoNCiMjIDkuMSBDYWxjdWxhdGUgYmFja2dyb3VuZHMNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9iZ30NCiNnYygpDQpiZyA9IGRlcml2ZV9HZW9NeF9iYWNrZ3JvdW5kKA0KICBub3JtID0gYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSwNCiAgcHJvYmVwb29sID0gZkRhdGEodGFyZ2V0X0RhdGEpJE1vZHVsZSwNCiAgbmVnbmFtZXMgPSBjKCJOZWdQcm9iZS1DVFAwMSIsIk5lZ1Byb2JlLUtpbG8iLCJOZWdhdGl2ZSBQcm9iZSIsICJOZWdQcm9iZS1XVFgiICkpDQogICNuZWduYW1lcyA9ICJOZWdQcm9iZS1XVFgiKQ0KDQpgYGANCg0KIyMgOS4yIExvYWQgY2VsbCBwcm9maWxlDQoNCkEgImNlbGwgcHJvZmlsZSBtYXRyaXgiIGlzIGEgcHJlLWRlZmluZWQgbWF0cml4IHRoYXQgc3BlY2lmaWVzIHRoZQ0KZXhwZWN0ZWQgZXhwcmVzc2lvbiBwcm9maWxlcyBvZiBlYWNoIGNlbGwgdHlwZSBpbiB0aGUgZXhwZXJpbWVudC4gVGhlDQpTcGF0aWFsRGVjb24gbGlicmFyeSBjb21lcyB3aXRoIG9uZSBzdWNoIG1hdHJpeCBwcmUtbG9hZGVkLCB0aGUNCiJTYWZlVE1FIiBtYXRyaXgsIGRlc2lnbmVkIGZvciBlc3RpbWF0aW9uIG9mIGltbXVuZSBhbmQgc3Ryb21hIGNlbGxzIGluDQp0aGUgdHVtb3IgbWljcm9lbnZpcm9ubWVudC4gKFRoaXMgbWF0cml4IHdhcyBkZXNpZ25lZCB0byBhdm9pZCBnZW5lcw0KY29tbW9ubHkgZXhwcmVzc2VkIGJ5IGNhbmNlciBjZWxsczsgc2VlIHRoZSBTcGF0aWFsRGVjb24gbWFudXNjcmlwdCBmb3INCmRldGFpbHMuKS4gT3RoZXJ3aXNlLCBsb2FkIHNwZWNpZmljIHByb2ZpbGVzIGZyb20NCjxodHRwczovL2dpdGh1Yi5jb20vTmFub3N0cmluZy1CaW9zdGF0cy9DZWxsUHJvZmlsZUxpYnJhcnkvdHJlZS9OZXdQcm9maWxlTWF0cmljZXM+DQoNCmBgYHtyIGxvYWRfY2VsbF9wcm9maWxlc30NCiNzYWZlVE1FDQpkYXRhKCJzYWZlVE1FIikNCmRhdGEoInNhZmVUTUUubWF0Y2hlcyIpDQpjdXJyZW50X2NlbGxfcHJvZmlsZTwtc2FmZVRNRQ0KDQojc2VlOiBodHRwczovL2dpdGh1Yi5jb20vTmFub3N0cmluZy1CaW9zdGF0cy9DZWxsUHJvZmlsZUxpYnJhcnkvdHJlZS9OZXdQcm9maWxlTWF0cmljZXMNCg0KY3VycmVudF9jZWxsX3Byb2ZpbGUgPC0gZG93bmxvYWRfcHJvZmlsZV9tYXRyaXgoc3BlY2llcyA9ICJIdW1hbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9ncm91cCA9ICJBZHVsdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeG5hbWUgPSAiS2lkbmV5X0hDQSIpDQoNCmhlYXRtYXAoc3dlZXAoY3VycmVudF9jZWxsX3Byb2ZpbGUsIDEsIGFwcGx5KGN1cnJlbnRfY2VsbF9wcm9maWxlLCAxLCBtYXgpLCAiLyIpLA0KICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpLCBjZXhDb2wgPSAwLjcpDQpgYGANCg0KIyA5LjMgUnVuIHNwYXRpYWwgZGVjb252b2x1dGlvbg0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3J1bn0NCiMgdmVjdG9yIGlkZW50aWZ5aW5nIHB1cmUgdHVtb3Igc2VnbWVudHM6DQojdGFyZ2V0X0RhdGEkaXN0dW1vciA9IHRhcmdldF9EYXRhJEFOTjMgPT0gIkNPUkUiICYgdGFyZ2V0X0RhdGEkQU5OMSA9PSAiUGFuQ0srIg0KcmVzID0gcnVuc3BhdGlhbGRlY29uKG9iamVjdCA9IHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsDQogICAgICAgICAgICAgICAgICAgICAgcmF3X2VsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgICAgICAgI2lzX3B1cmVfdHVtb3IgPSB0YXJnZXRfRGF0YSRpc3R1bW9yLA0KICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLA0KICAgICAgICAgICAgICAgICAgICAgIFggPSBjdXJyZW50X2NlbGxfcHJvZmlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAjY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgI25fdHVtb3JfY2x1c3RlcnMgPSA1LCAgICAgICAgICAgICAgICAgICAgICAjIGhvdyBtYW55IGRpc3RpbmN0IHR1bW9yIHByb2ZpbGVzIHRvIGFwcGVuZCB0byBzYWZlVE1FDQogICAgICAgICAgICAgICAgICAgICAgYWxpZ25fZ2VuZXMgPSBUUlVFKQ0KDQoNCmBgYA0KDQojIDkuMy4xIFNwYXRpYWwgZGVjb252b2x1dGlvbiBoZWF0bWFwcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQojIyBBYnVuZGFuY2UNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9oZWF0bWFwLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgTk9URTogY2hlY2sgY2x1c3RlcmluZy4uIHdoeSBkaWZmZXJlbnQ/DQoNCiNzZXQgZGlzcGxheSB0aHJlc2hvbGRzDQp0aHJlc2ggPC0gc2lnbmlmKHF1YW50aWxlKHJlcyRiZXRhLCAwLjk3KSwgMikNCg0KIyBwbG90IHN0b3JlZCB0byBrZWVwIGNsdXN0ZXJpbmcgZm9yIGxhdGVyDQpwMTwtcGhlYXRtYXAocG1pbih0KHJlcyRiZXRhKSx0aHJlc2gpLA0KICAgICAgICAgI3NjYWxlID0gInJvdyIsIA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMocm91bmQoc2VxKDAsIHRocmVzaCwgbGVuZ3RoLm91dCA9IDUpKVstNV0sIHRocmVzaCksDQogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYyhyb3VuZChzZXEoMCwgdGhyZXNoLCBsZW5ndGgub3V0ID0gNSkpWy01XSwgcGFzdGUwKCJBYnVuZGFuY2Ugc2NvcmVzLFxudHJ1bmNhdGVkIGFib3ZlIGF0ICIsIHRocmVzaCkpLA0KICAgICAgICAgI2JyZWFrcyA9IHNlcSgwLCA1LCAxKSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsImRhcmtibHVlIikpKDEwMCksDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10NCiAgICAgICAgICkNCiNwMQ0KYGBgDQoNCiMjIFByb3BvcnRpb25hbA0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3Byb3BoZWF0bWFwLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgcHJvcG9ydGlvbnM6DQpwcm9wcyA8LSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApDQoNCnAyPC1waGVhdG1hcCh0KHByb3BzKSwNCiAgICAgICAgICNzY2FsZSA9ICJyb3ciLCANCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDEyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgYW5nbGVfY29sID0gIjkwIiwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSByb3VuZChzZXEoMCwgbWF4KHByb3BzKSAqIDAuOTksIGxlbmd0aC5vdXQgPSA1KSwgMiksDQogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYyhyb3VuZChzZXEoMCwgbWF4KHByb3BzKSwgbGVuZ3RoLm91dCA9IDUpLCAyKVstNV0sICJQcm9wb3J0aW9uIG9mIGFsbFxuZml0dGVkIHBvcHVsYXRpb25zIiksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJkYXJrYmx1ZSIpKSgxMDApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQojcDINCg0KYGBgDQoNCiMjIFNjYWxlZA0KDQpgYGB7ciBzcGF0aWFsX2RlY29uX3NjYWxlZGhlYXRtYXAsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1fQ0KIyBzY2FsZWQgYWJ1bmRhbmNlczoNCmVwc2lsb24gPC0gbWluKHJlcyRiZXRhW3JlcyRiZXRhID4gMF0pDQptYXQgPC0gc3dlZXAocmVzJGJldGEsIDEsIHBtYXgoYXBwbHkocmVzJGJldGEsIDEsIG1heCksIGVwc2lsb24pLCAiLyIpDQoNCnBoZWF0bWFwKHQobWF0KSwNCiAgICAgICAgICNzY2FsZSA9ICJyb3ciLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAzLA0KICAgICAgICAgY3V0cmVlX3Jvd3MgPSAzLA0KICAgICAgICAgZm9udHNpemVfcm93ID0gMTIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBhbmdsZV9jb2wgPSAiOTAiLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICAjY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgbGVnZW5kX2JyZWFrcyA9IGMocm91bmQoc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSA1KSwgMilbLTVdLCAxKSwNCiAgICAgICAgIGxlZ2VuZF9sYWJlbHMgPSBjKHJvdW5kKHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gNSksIDIpWy01XSwgIlNjYWxlZCBhYnVuZGFuY2VcbihyYXRpbyB0byBtYXgpIiksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJkYXJrYmx1ZSIpKSgxMDApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQpgYGANCg0KIyA5LjQgQmFycGxvdHMgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KIyMgYWJ1bmRhbmNlDQoNCmBgYHtyIFNEX2FidW5kYW5jZV9iYXJwbG90LCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgZGVmaW5lIHZhcmlhYmxlcyB0byBzaG93IGluIGhlYXRtYXBzOg0KcERhdGEodGFyZ2V0X0RhdGEpJHJlZ2lvbiA8LSANCiAgICBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjMsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4zKSkgICAjIE11c3QgYmUgbWFudWFsPw0KcERhdGEodGFyZ2V0X0RhdGEpJGNsYXNzIDwtIA0KICAgIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSwgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjEpKSAgICMgTXVzdCBiZSBtYW51YWw/DQoNCnZhcmlhYmxlc190b19wbG90IDwtIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikgICAgICAgICAgICAgICAgICAgICAjIE11c3QgYmUgbWFudWFsPw0KDQojZGVmaW5lIGNvbG9ycw0KDQojIG9ubHkgZm9yIHNhZmVUTUUgY29sb3JzDQojY29sIDwtIGNlbGxjb2xzDQoNCiNjdXN0b20gY2VsbWF0cml4DQojZ2V0IGxhcmdlIG51bWJlciBvZiBjb2xvcnMNCnF1YWxfY29sX3BhbHMgPSBicmV3ZXIucGFsLmluZm9bYnJld2VyLnBhbC5pbmZvJGNhdGVnb3J5ID09ICdxdWFsJyxdDQpjb2xfdmVjdG9yID0gdW5pcXVlKHVubGlzdChtYXBwbHkoYnJld2VyLnBhbCwgcXVhbF9jb2xfcGFscyRtYXhjb2xvcnMsIHJvd25hbWVzKHF1YWxfY29sX3BhbHMpKSkpDQpjZWxsdHlwZXM8LXNhbXBsZShjb2xfdmVjdG9yLGxlbmd0aChjb2xuYW1lcyhyZXMkYmV0YSkpKQ0KbmFtZXMoY2VsbHR5cGVzKTwtY29sbmFtZXMocmVzJGJldGEpDQpjb2w8LWNlbGx0eXBlcw0KDQoNCiN0ZW1wZml4IGZvciBhYmJyZXZpYXRlZCBhbmQgbm93IG1pc21hdGNoaW5nIGFubm90YXRpb25zDQojY2FuIGp1c3QgdXNlIGFubiBkYXRhZnJhbWU/DQojdG1wYW5uPC1jYmluZChBTk4xLEFOTjIsU04pDQp0bXBhbm4gPC0gcERhdGEodGFyZ2V0X0RhdGEpW2Fubl9uYW1lc10NCg0KDQoNCmxheW91dChtYXRyaXgoYygxLCAyLCAzLCAzKSwgbnJvdyA9IDIpLA0KICAgICAgIHdpZHRocyA9IGMoMTAsIDMsIDEwLCAzKSwNCiAgICAgICBoZWlnaHRzID0gYygxLCA4LCAxMCksDQogICAgICApDQoNCnBhcihtYXIgPSBjKDAsIDguMiwgMCwgMC4yKSkNCnBsb3QocDEkdHJlZV9jb2wsIGxhYmVscyA9IEYsIG1haW4gPSAiIiwgeWxhYiA9ICIiLCB5YXh0ID0gIm4iKQ0KcGFyKG1hciA9IGMoMTUsIDgsIDAsIDApKQ0KDQojIGRhdGEgdG8gcGxvdDoNCm1hdCA8LSB0KHJlcyRiZXRhKVssIHAxJHRyZWVfY29sJG9yZGVyXQ0KIyBpbmZlciBzY2FsZSBvZiBuZWdhdGl2ZSB5LWF4aXMgZm9yIGFubm90YXRpb24gY29sb3JiYXJzDQp5bWluIDwtIC1tYXgoY29sU3VtcyhtYXQpKSAqIDAuMTUNCmlmICghaXMuZmluaXRlKHltaW4pKSB7DQogIHltaW4gPC0gMA0KfQ0KDQojIGRyYXcgYmFycGxvdDoNCmJwIDwtIGJhcnBsb3QobWF0LA0KICAgICAgICAgICAgICBjZXgubGFiID0gMS41LA0KICAgICAgICAgICAgICBjb2wgPSBjb2wsIGJvcmRlciA9IE5BLA0KICAgICAgICAgICAgICBjZXgubmFtZXMgPSAxLjEsDQogICAgICAgICAgICAgIGxhcyA9IDIsIG1haW4gPSAiIiwgeWxhYiA9ICJBYnVuZGFuY2Ugc2NvcmVzIiwNCiAgICAgICAgICAgICAgeWxpbSA9IGMoeW1pbiwgbWF4KGNvbFN1bXMobWF0KSkpDQopDQoNCg0KIyBhZGQgY29sb3IgYmFycyBmb3IgYW5ub3RhdGlvbnMNCmZvciAobmFtZSBpbiByZXYodmFyaWFibGVzX3RvX3Bsb3QpKSB7DQogIHlyYW5nZSA8LSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMSlbbWF0Y2gobmFtZSwgdmFyaWFibGVzX3RvX3Bsb3QpICsgYygwLCAxKV0NCiAgeHdpZHRoIDwtIChicFsyXSAtIGJwWzFdKSAvIDINCiAgZm9yIChpIGluIDE6bmNvbChtYXQpKSB7DQogICAgcmVjdChicFtpXSAtIHh3aWR0aCwgeXJhbmdlWzJdLCBicFtpXSArIHh3aWR0aCwgeXJhbmdlWzFdLA0KICAgICAgICAgIyBib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW3NlZ21lbnRBbm5vdGF0aW9uc1ttYXRjaChjb2xuYW1lcyhtYXQpW2ldLCBzZWdtZW50QW5ub3RhdGlvbnMkc2VnbWVudElEKSwgbmFtZV1dDQogICAgICAgICAjYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVthbm5bcDEkdHJlZV9jb2wkb3JkZXJbaV0sIG5hbWVdXQ0KICAgICAgICAgYm9yZGVyID0gTkEsIGNvbCA9IGNvbG9yX2xpc3RbW25hbWVdXVt0bXBhbm5bY29sbmFtZXMobWF0KVtpXSwgbmFtZV1dDQogICAgKQ0KICB9DQp9DQpheGlzKDIsDQogICAgIGF0ID0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpWy1jKDEsIGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKV0sDQogICAgIGxhcyA9IDIsIGxhYmVscyA9IHZhcmlhYmxlc190b19wbG90LCBsdHkgPSAwLCBjZXguYXhpcyA9IDEuMg0KKQ0KDQojZHJhdyBhIGxlZ2VuZDoNCnBhcihtYXIgPSBjKDAuMSwgMC4xLCAwLjEsIDAuMSkpDQpmcmFtZSgpDQpsZWdlbmRjb2xzIDwtIGxlZ2VuZG5hbWVzIDwtIGMoKQ0KI2ZvciAobmFtZSBpbiByZXYobmFtZXMoYW5uX2NvbG9ycykpKSB7DQpmb3IgKG5hbWUgaW4gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKSkgew0KICBsZWdlbmRjb2xzIDwtIGMobGVnZW5kY29scywgTkEsIGNvbG9yX2xpc3RbW25hbWVdXSwgTkEpDQogIGxlZ2VuZG5hbWVzIDwtIGMobGVnZW5kbmFtZXMsIG5hbWUsIG5hbWVzKGNvbG9yX2xpc3RbW25hbWVdXSksIE5BKQ0KfQ0KbGVnZW5kKCJjZW50ZXIiLA0KICAgICAgIHBjaCA9IDE1LA0KICAgICAgIGNleCA9IDEuNSwNCiAgICAgICBjb2wgPSBjKGxlZ2VuZGNvbHMsIHJlcChOQSwgMSksIHJldihjb2wpKSwNCiAgICAgICBsZWdlbmQgPSBjKGxlZ2VuZG5hbWVzLCAiQ2VsbCB0eXBlIiwgcmV2KG5hbWVzKGNvbCkpKSwNCikNCmBgYA0KDQojIyBwcm9wb3J0aW9uYWwNCg0KYGBge3IgU0RfcHJvcF9iYXJwbG90LCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgZGVmaW5lIHZhcmlhYmxlcyB0byBzaG93IGluIGhlYXRtYXBzOg0KdmFyaWFibGVzX3RvX3Bsb3QgPC0gYygic2xpZGVfbmFtZSIsICJBTk4xIiwgIkFOTjMiKQ0KDQpsYXlvdXQobWF0cml4KGMoMSwgMiwgMywgMyksIG5yb3cgPSAyKSwNCiAgICAgICB3aWR0aHMgPSBjKDEwLCAzLCAxMCwgMyksDQogICAgICAgaGVpZ2h0cyA9IGMoMSwgOCwgMTApLA0KICAgICAgKQ0KDQpwYXIobWFyID0gYygwLCA4LjIsIDAsIDAuMikpDQpwbG90KHAyJHRyZWVfY29sLCBsYWJlbHMgPSBGLCBtYWluID0gIiIsIHlsYWIgPSAiIiwgeWF4dCA9ICJuIikNCnBhcihtYXIgPSBjKDE1LCA4LCAwLCAwKSkNCg0KIyBkYXRhIHRvIHBsb3Q6DQptYXQgPC0gdChyZXMkcHJvcF9vZl9ub250dW1vcilbLCBwMiR0cmVlX2NvbCRvcmRlcl0NCiAgbWF0IDwtIHJlcGxhY2UobWF0LCBpcy5uYShtYXQpLCAwKQ0KICAjIGluZmVyIHNjYWxlIG9mIG5lZ2F0aXZlIHktYXhpcyBmb3IgYW5ub3RhdGlvbiBjb2xvcmJhcnMNCiAgeW1pbiA8LSAtMC4xNQ0KDQojIGRyYXcgYmFycGxvdDoNCmJwIDwtIGJhcnBsb3QobWF0LA0KICAgICAgICAgICAgICBjZXgubGFiID0gMS41LA0KICAgICAgICAgICAgICBjb2wgPSBjb2wsIGJvcmRlciA9IE5BLA0KICAgICAgICAgICAgICBjZXgubmFtZXMgPSAxLjEsDQogICAgICAgICAgICAgIGxhcyA9IDIsIG1haW4gPSAiIiwgeWxhYiA9ICJQcm9wb3J0aW9uIG9mIGZpdHRlZCBjZWxscyIsDQogICAgICAgICAgICAgIHlsaW0gPSBjKHltaW4sIG1heChjb2xTdW1zKG1hdCkpKQ0KKQ0KDQojIGFkZCBjb2xvciBiYXJzIGZvciBhbm5vdGF0aW9ucw0KZm9yIChuYW1lIGluIHJldih2YXJpYWJsZXNfdG9fcGxvdCkpIHsNCiAgeXJhbmdlIDwtIHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAxKVttYXRjaChuYW1lLCB2YXJpYWJsZXNfdG9fcGxvdCkgKyBjKDAsIDEpXQ0KICB4d2lkdGggPC0gKGJwWzJdIC0gYnBbMV0pIC8gMg0KICBmb3IgKGkgaW4gMTpuY29sKG1hdCkpIHsNCiAgICByZWN0KGJwW2ldIC0geHdpZHRoLCB5cmFuZ2VbMl0sIGJwW2ldICsgeHdpZHRoLCB5cmFuZ2VbMV0sDQogICAgICAgICAjIGJvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bc2VnbWVudEFubm90YXRpb25zW21hdGNoKGNvbG5hbWVzKG1hdClbaV0sIHNlZ21lbnRBbm5vdGF0aW9ucyRzZWdtZW50SUQpLCBuYW1lXV0NCiAgICAgICAgICNib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW2FubltwMiR0cmVlX2NvbCRvcmRlcltpXSwgbmFtZV1dDQogICAgICAgICBib3JkZXIgPSBOQSwgY29sID0gY29sb3JfbGlzdFtbbmFtZV1dW3RtcGFubltjb2xuYW1lcyhtYXQpW2ldLCBuYW1lXV0NCiAgICApDQogIH0NCn0NCmF4aXMoMiwNCiAgICAgYXQgPSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMilbLWMoMSwgbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpXSwNCiAgICAgbGFzID0gMiwgbGFiZWxzID0gdmFyaWFibGVzX3RvX3Bsb3QsIGx0eSA9IDAsIGNleC5heGlzID0gMS4yDQopDQoNCg0KI2RyYXcgYSBsZWdlbmQ6DQpwYXIobWFyID0gYygwLjEsIDAuMSwgMC4xLCAwLjEpKQ0KZnJhbWUoKQ0KbGVnZW5kY29scyA8LSBsZWdlbmRuYW1lcyA8LSBjKCkNCiNmb3IgKG5hbWUgaW4gcmV2KG5hbWVzKGFubl9jb2xvcnMpKSkgew0KZm9yIChuYW1lIGluIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikpIHsNCiAgbGVnZW5kY29scyA8LSBjKGxlZ2VuZGNvbHMsIE5BLCBjb2xvcl9saXN0W1tuYW1lXV0sIE5BKQ0KICBsZWdlbmRuYW1lcyA8LSBjKGxlZ2VuZG5hbWVzLCBuYW1lLCBuYW1lcyhjb2xvcl9saXN0W1tuYW1lXV0pLCBOQSkNCn0NCmxlZ2VuZCgiY2VudGVyIiwNCiAgICAgICBwY2ggPSAxNSwNCiAgICAgICBjZXggPSAxLjQsDQogICAgICAgY29sID0gYyhsZWdlbmRjb2xzLCByZXAoTkEsIDEpLCByZXYoY29sKSksDQogICAgICAgbGVnZW5kID0gYyhsZWdlbmRuYW1lcywgIkNlbGwgdHlwZSIsIHJldihuYW1lcyhjb2wpKSksDQopDQpgYGANCg0KIyAxMSBDTlYNCkNvcHkgTnVtYmVyIFZhcmlhdGlvbiBhbmFseXNpcyB3aXRoIHRoZSBSIHNvZnR3YXJlIHBhY2thZ2UgaW5mZXJDTlYgKGh0dHBzOi8vZ2l0aHViLmNvbS9icm9hZGluc3RpdHV0ZS9pbmZlckNOVi93aWtpKS4NCg0KSW5mZXJDTlYgaXMgdXNlZCB0byBleHBsb3JlIHR1bW9yIHNpbmdsZSBjZWxsIFJOQS1TZXEgZGF0YSB0byBpZGVudGlmeSBldmlkZW5jZSBmb3Igc29tYXRpYyBsYXJnZS1zY2FsZSBjaHJvbW9zb21hbCBjb3B5IG51bWJlciBhbHRlcmF0aW9ucywgc3VjaCBhcyBnYWlucyBvciBkZWxldGlvbnMgb2YgZW50aXJlIGNocm9tb3NvbWVzIG9yIGxhcmdlIHNlZ21lbnRzIG9mIGNocm9tb3NvbWVzLiBUaGlzIGlzIGRvbmUgYnkgZXhwbG9yaW5nIGV4cHJlc3Npb24gaW50ZW5zaXR5IG9mIGdlbmVzIGFjcm9zcyBwb3NpdGlvbnMgb2YgdHVtb3IgZ2Vub21lIGluIGNvbXBhcmlzb24gdG8gYSBzZXQgb2YgcmVmZXJlbmNlICdub3JtYWwnIGNlbGxzLiBBIGhlYXRtYXAgaXMgZ2VuZXJhdGVkIGlsbHVzdHJhdGluZyB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBpbnRlbnNpdGllcyBhY3Jvc3MgZWFjaCBjaHJvbW9zb21lLCBhbmQgaXQgb2Z0ZW4gYmVjb21lcyByZWFkaWx5IGFwcGFyZW50IGFzIHRvIHdoaWNoIHJlZ2lvbnMgb2YgdGhlIHR1bW9yIGdlbm9tZSBhcmUgb3Zlci1hYnVuZGFudCBvciBsZXNzLWFidW5kYW50IGFzIGNvbXBhcmVkIHRvIHRoYXQgb2Ygbm9ybWFsIGNlbGxzLg0KDQpQZXIgcGF0aWVudCBhIGNudiBhbmFseXNpcyB3aXRoIGEgcHJvdmlkZWQgcmVmZXJlbmNlIGdyb3VwLiBUaGUgZ2VuZSBvcmRlciBpcyBtYWRlIGZyb20gdGhlIHByb2plY3RzIC5wa2MgZmlsZS4NCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9uIHdoZXJlIHRoZSBDTlYgYW5hbHlzaXMgc2hvdWxkIGxvb2sgYXQgc3BlY2lmaWNhbGx5IGFzIGdyb3VwIGFuZCBzcGVjaWZ5IGEgc3ViZ3JvdXAgYXMgZ3JvdXBfZmlsdGVyIGlmIHRoZSBncm91cCBvZiBpbnRlcmVzdCBpcyBhIHN1Ymdyb3VwIGluc2lkZSB0aGUgYW5ub3RhdGlvbi4gRm9yIGV4YW1wbGUgaWYgdGhlIENOViBhbmFseXNpcyBuZWVkcyB0byBiZSBvbmx5IG9uIHR1bW9yIHJlZ2lvbnMgdGhlIGdyb3VwIHdvdWxkIGJlIEFOTlgoQU5OIGNvbHVtbiB3aXRoIHRoZSBpbmZvIGFib3V0IHRoZSB0dW1vciByZWdpb25zKSBhbmQgZ3JvdXBfZmlsdGVyIFBhbkNLKy4gDQpUaGUgYW5ub3RhdGlvbiB0aGF0IHJlZmVyZW5jZXMgdGhlIHBhdGllbnRzIGFzIHBhdGllbnRzLiBJZiB0aGVyZSBhcmUgbm8gcGF0aWVudHMgbGVhdmUgdGhlIHBhcmFtZXRlciBlbXB0eSBhcyAiIi4NClRoZSBhbm5vdGF0aW9uIHRoYXQgc2hvdWxkIGJlIGluY2x1ZGVkIGluIHRoZSByZXN1bHRzIGluY2x1ZGluZyBhIHJlZmVyZW5jZSBzZXQgYXMgY252X3RhcmdldC4NClRoaXMgd2lsbCBjcmVhdGUgcGF0aWVudCBzcGVjaWZpYyBmaWxlcyB3aXRoIGFsbCB0aGUgaW5mb3JtYXRpb24gaW5mZXJDTlYgbmVlZHMuDQoNCmBgYHtyIGNyZWF0ZSBjbnYgZmlsZXMsIGluY2x1ZGU9RkFMU0V9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmdyb3VwIDwtICIiDQpncm91cF9maWx0ZXIgPC0gIiINCg0KcGF0aWVudHMgPC0gIiINCg0KY252X3RhcmdldCA8LSAiQU5OMSINCnByaW50KHBhc3RlKCJTaG93IENOViBvbiIsIGNudl90YXJnZXQsICJ3aXRoIHRoZSBzcGVjaWZpY2l0eSBvbiB0aGUgZ3JvdXBzIChpZiBhbnkpOiIsIGdyb3VwLCAiOyIsIGdyb3VwX2ZpbHRlcikpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KaWYgKCFwYXRpZW50cyA9PSAiIikgew0KICBwYXRpZW50X2xpc3QgPC0gdW5pcXVlKHRhcmdldF9EYXRhW1twYXRpZW50c11dKQ0KfSBlbHNlew0KICBwYXRpZW50X2xpc3QgPC0gImR1bW15Ig0KfQ0KDQpmaWxlIDwtIHJlYWRMaW5lcyhQS0NGaWxlcykNCmZpbGUgPC0gZ3N1YignICcsICcnLA0KICAgICAgICBnc3ViKCciJywgJycsIGZpbGUpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgYW5kIHJlbW92ZSBzcGFjZQ0KDQpkaXNwbGF5IDwtIGZpbGVbZ3JlcGwoIkRpc3BsYXlOYW1lIiwgZmlsZSwgZml4ZWQgPSBUUlVFKV0gICAgICAgICAjIEdyYWIgZGlzcGxheSBuYW1lcw0KZGlzcGxheSA8LSBnc3ViKCdEaXNwbGF5TmFtZScsICIiLA0KICAgICAgICAgICBnc3ViKCciJywgIiIsDQogICAgICAgICAgIGdzdWIoJzonLCAiIiwNCiAgICAgICAgICAgZ3N1YignLCcsICIiLA0KICAgICAgICAgICBnc3ViKCcgJywgIiIsDQogICAgICAgICAgIGdzdWIoJ18wMScsICIiLCBkaXNwbGF5KSkpKSkpICAgICAgICAgICAgICAgICAgICAgICAgICAjIEdyYWIgb25seSB0aGUgbmFtZXMNCmRpc3BsYXkgPC0gdW5pcXVlKGRpc3BsYXkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIGR1cGxpY2F0ZXMNCg0KY2hyIDwtIGZpbGVbZ3JlcCgiR2Vub21lQ29vcmRpbmF0ZXMiLCBmaWxlKSsxXSAgICAgICAgICAgICAgICAgICAgIyBHZXQgdGhlIGNociBwb3NpdGlvbnMgdW5kZXIgdGhlIEdlbm9tZUNvb3JkaW5hdGVzIGxpbmUNCmNociA8LWdzdWIoJywnLCAiIiwgY2hyKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUmVtb3ZlIHVud2FudGVkIHN5bWJvbHMNCg0KcG9zaXRpb25zIDwtIGRhdGEuZnJhbWUoDQogIG5hbWUgPSBkaXNwbGF5LA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTd2FwIHR5cGVzIHRvIHJlY29nbml6ZSBzcGxpdCBjb2x1bW5zIGFzIHNpbmd1bGFyIGNvbHVtbnMNCg0KcG9zaXRpb25zJG9yZGVyIDwtIGFzLm51bWVyaWMoZ3N1YignY2hyJywgJycsIHBvc2l0aW9ucyRjaHIuMSkpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zW29yZGVyKHBvc2l0aW9ucyRvcmRlciwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXSAjIE9yZGVyIHRoZSBjaHJvbW9zb21lcy4NCnBvc2l0aW9ucyRvcmRlciA8LSBOVUxMDQoNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gTlVMTA0Kcm93bmFtZXMocG9zaXRpb25zKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlckNOViByZXF1aXJlcyBubyBjb2x1bW4gYW5kIHJvdyBuYW1lcw0KDQp3cml0ZS50YWJsZShwb3NpdGlvbnMsICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwNCiAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdyaXRlIGF3YXkNCg0KDQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIGlmIChncm91cF9maWx0ZXIgIT0gIiIpIHsNCiAgICBncm91cF9pbnRlcmVzdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YVtbIlNhbXBsZUlEIl1dW3RhcmdldF9EYXRhW1tncm91cF1dID09IGdyb3VwX2ZpbHRlciAmIHRhcmdldF9EYXRhW1twYXRpZW50c11dID09IHBhdGllbnRdDQogIH0gZWxzZSBpZiAobGVuZ3RoKHBhdGllbnRfbGlzdCkgPiAxKSB7DQogICAgZ3JvdXBfaW50ZXJlc3QgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGFbWyJTYW1wbGVJRCJdXVt0YXJnZXRfRGF0YVtbcGF0aWVudHNdXSA9PSBwYXRpZW50XQ0KICB9IGVsc2Ugew0KICAgIGdyb3VwX2ludGVyZXN0IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhW1siU2FtcGxlSUQiXV0NCiAgfQ0KICANCiAgY291bnRzIDwtIHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dDQogIGNvbG5hbWVzKGNvdW50cykgPC0gZ3N1YignLmRjYycsICcnLGNvbG5hbWVzKGNvdW50cykpDQogIGNvdW50cyA8LSBjb3VudHNbLGNvbG5hbWVzKGNvdW50cykgJWluJSBjKGdyb3VwX2ludGVyZXN0KV0NCiAgd3JpdGUudGFibGUoY291bnRzLCBwYXN0ZTAocGF0aWVudCwgIi5Db3VudHMudHN2IiksIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KICBhbm5vdGF0aW9uIDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQogIGFubm90YXRpb24kU2FtcGxlSUQgPC0gZ3N1YignLmRjYycsJycsIHJvd25hbWVzKGFubm90YXRpb24pKQ0KICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVJRCAlaW4lIGMoZ3JvdXBfaW50ZXJlc3QpLF0NCiAgI2Fubm90YXRpb24gPC0gYW5ub3RhdGlvbltjKCJTYW1wbGVJRCIsICJzZWdtZW50IildICAgICAgICAgICAgICAgICAgIyBTZWxlY3QgU2FtcGxlSUQgYW5kIHRoZSBuZWVkZWQgYW5ub3RhdGlvbiAoQ05WIHJlcXVpcmVzIFNhbXBsZUlEIGFuZCBvbmx5IDEgYW5ub3RhdGlvbikNCiAgYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgY252X3RhcmdldCldDQogIHJvd25hbWVzKGFubm90YXRpb24pIDwtIE5VTEwNCiAgY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQogIHdyaXRlLnRhYmxlKGFubm90YXRpb24sIHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpLA0KICAgICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgICBxdW90ZSA9IEZBTFNFLA0KICAgICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIE1ha2UgYW5ub3RhdGlvbiBmaWxlDQp9DQpgYGANCg0KU2VsZWN0IHRoZSByZWZlcmVuY2Ugc2V0IGluIHJlZmVyZW5jZS4gVGhpcyBjYW4gYmUgbW9yZSB0aGFuIG9uZS4gRXZlcnkgcGF0aWVudCB3aXRob3V0IGl0cyBvd24gcmVmZXJlbmNlIHdpbGwgbm90IGJlIGFuYWx5c2VkLg0KDQpgYGB7ciBydW4gQ05WLCBlY2hvPVQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KIyMjIyMjIyMjUEFSQU1FVEVSUyMjIyMjIyMjDQpyZWZlcmVuY2UgPC0gYygibm9ybWFsIikNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpmYWlsZWQgPC0gYygpDQpjbnZfbGlzdCA8LSBsaXN0KCkNCg0KZm9yIChwYXRpZW50IGluIHBhdGllbnRfbGlzdCkgew0KICAjIE5hbWUgb3V0cHV0IGZvbGRlcg0KICBvdXRfZGlyIDwtIHBhc3RlMChwYXRpZW50LCAiX0NOViIpDQogIGlmIChzdHJfZGV0ZWN0KHBhc3RlKHJlYWRMaW5lcyhwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSksIGNvbGxhcHNlID0gJycpLCByZWZlcmVuY2UpID09IEZBTFNFKSB7DQogICAgZmFpbGVkIDwtIGMoZmFpbGVkLCBwYXRpZW50KQ0KICAgIG5leHQNCiAgICB9DQoNCiAgIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KICBpbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0gcGFzdGUwKHBhdGllbnQsICIuQ291bnRzLnRzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uc19maWxlPSBwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9vcmRlcl9maWxlPSAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICNnZW5lX29yZGVyX2ZpbGU9ICJnZW5jb2RlX3YxOV9nZW5lX3Bvcy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfZXhjbHVkZSA9IGMoImNock0iKSkjYygiY2hyTSIpKSAjIERlZmF1bHQgZXhjbHVkZXMgY2hyWCwgY2hyWSBhbmQgY2hyTS4gQnkgb25seSBwaWNraW5nIGNock0geW91IGluY2x1ZGUgdGhlIFggYW5kIFkgY2hyb21vc29tZXMuDQoNCiAgIyBwZXJmb3JtIGluZmVyY252IG9wZXJhdGlvbnMgdG8gcmV2ZWFsIGNudiBzaWduYWwuIEZvciBhbGwgb3B0aW9uczogaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9icm9hZGluc3RpdHV0ZS9pbmZlcmNudi9tYW4vcnVuLmh0bWwNCiAgI2Nudl9saXN0W1twYXRpZW50XV0NCiAgaW5mZXJjbnZfb2JqIDwtIGluZmVyY252OjpydW4oaW5mZXJjbnZfb2JqLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0X2Rpcj0gb3V0X2RpciwgICMgZGlyIGlzIGF1dG8tY3JlYXRlZCBmb3Igc3RvcmluZyBvdXRwdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9ieV9ncm91cHM9RkFMU0UsICAgIyBjbHVzdGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVub2lzZT1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0dW1vcl9zdWJjbHVzdGVyX3BhcnRpdGlvbl9tZXRob2QgPSBjKCJxbm9ybSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5c2lzX21vZGUgPSAic3ViY2x1c3RlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3Bsb3Q9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMsZGVidWc9VFJVRQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICBwbG90X2NudihpbmZlcmNudl9vYmosDQogICAgICAgICAgb3V0X2RpciA9IHBhc3RlMChwYXRpZW50LCAiX0NOViIpLA0KICAgICAgICAgIHRpdGxlID0gImluZmVyQ05WIiwNCiAgICAgICAgICBvYnNfdGl0bGUgPSAiT2JzZXJ2YXRpb25zIChDZWxscykiLA0KICAgICAgICAgIHJlZl90aXRsZSA9ICJSZWZlcmVuY2VzIChDZWxscykiLA0KICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzID0gRkFMU0UsDQogICAgICAgICAgY2x1c3Rlcl9yZWZlcmVuY2VzID0gRkFMU0UsDQogICAgICAgICAgcGxvdF9jaHJfc2NhbGUgPSBGQUxTRSwNCiAgICAgICAgICAjY2hyX2xlbmd0aHMgPSBOVUxMLA0KICAgICAgICAgIGtfb2JzX2dyb3VwcyA9IDEsDQogICAgICAgICAgY29udGlnX2NleCA9IDEuNSwNCiAgICAgICAgICAjeC5jZW50ZXIgPSBtZWFuKGluZmVyY252X29iakBleHByLmRhdGEpLA0KICAgICAgICAgIHgucmFuZ2UgPSAiYXV0byIsDQogICAgICAgICAgI2hjbHVzdF9tZXRob2QgPSAid2FyZC5EIiwNCiAgICAgICAgICBvdXRwdXRfZmlsZW5hbWUgPSAiaW5mZXJjbnYiLA0KICAgICAgICAgIG91dHB1dF9mb3JtYXQgPSAicG5nIiwNCiAgICAgICAgICBwbmdfcmVzID0gMzAwDQogICAgICAgICAgKQ0KfQ0KYGBgDQoNCmBgYHtyIHNob3cgQ05WLCBmaWcuaGVpZ2h0PTMwLCBmaWcud2lkdGg9MjV9DQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogIHByaW50KHBhc3RlKGdyb3VwX2ZpbHRlciwgcGF0aWVudCkpDQogIGlmIChwYXRpZW50ICVpbiUgZmFpbGVkKSB7DQogICAgcHJpbnQoIiBeICAgIFBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQogIGltZyA8LSByZWFkUE5HKHBhc3RlKHBhc3RlMChwYXRpZW50LCAiX0NOViIpLCAiL2luZmVyY252LnBuZyIsIHNlcCA9ICIiKSkNCiAgZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCiAgZ3JpZDo6Z3JpZC5yYXN0ZXIoaW1nKQ0KfQ0KYGBgDQoNCiMgMTEuMSBEZW5kcm9ncmFtDQoNCkNsb3NlciBsb29rIGF0IHRoZSBkZW5kcm9ncmFtIGZyb20gdGhlIENOViBhbmFseXNpcyBwZXIgcGF0aWVudCB3aGVyZSB0aGUgbm9kZXMgYXMgd3JpdHRlbiBudW1iZXJzLiBVc2UgdGhlIG51bWJlcnMgdG8gc2VsZWN0IHN1Ymdyb3VwcyBmb3IgZnVydGhlciBhbmFseXNpcyBpbiAndC10ZXN0IG9uIHR3byBzdWJncm91cHMnLg0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQojb3V0X2RpciA8LSAiVDFfTkFOT18wMTJfQ05WIg0KdHJlZXMgPC0gbGlzdCgpDQoNCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgaWYgKHBhdGllbnQgJWluJSBmYWlsZWQpIHsNCiAgICAjcHJpbnQoIlBhdGllbnQgY29udGFpbmVkIG5vIHJlZmVyZW5jZSBncm91cCIpDQogICAgbmV4dA0KICB9DQp0cmVlIDwtIHJlYWQudHJlZShwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uc19kZW5kcm9ncmFtLnR4dCIsIHNlcCA9ICIiKSkNCm9idiA8LSByZWFkLmNzdihwYXN0ZShwYXRpZW50LCJfQ05WL2luZmVyY252Lm9ic2VydmF0aW9uX2dyb3VwaW5ncy50eHQiLCBzZXAgPSAiIiksIHNlcD0iIikNCnRyZWVzW1twYXRpZW50XV0gPC0gZ2d0cmVlKA0KICB0cmVlLCBsYWRkZXJpemU9RikgKw0KICBnZW9tX3RyZWVzY2FsZSgpICsNCiAgZ2VvbV90aXBsYWIoY29sb3I9b2J2JEFubm90YXRpb24uQ29sb3IsIGhqdXN0PS0uMikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICdvZmYnKSArDQogIHRoZW1lX3RyZWUyKHBsb3QubWFyZ2luPW1hcmdpbig2LCAyMDAsIDYsIDYpKSArDQogIGdlb21fdGV4dDIoYWVzKGxhYmVsPW5vZGUpLCBoanVzdD0tLjMsIHNpemUgPSAzKSArDQogIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXRpZW50KQ0KfQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnM9dHJlZXMsbmNvbD0zKQ0KYGBgDQoNCiMjIHQtdGVzdCBjaHIgd2l0aCBncm91cHMNCg0KQ29tcGFyZSB0aGUgY29udHJhc3Qgd2l0aGluIGEgY2hyb21vc29tZS4gU2VsZWN0IGEgY2hyb21vc29tZSwgY29udHJhc3QsIGFuZCBwYXRpZW50IG9mIGludGVyZXN0IHRvIHJ1biBhIHQtdGVzdCBvbi4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNociA8LSAiY2hyWCIgIyBTZWxlY3QgY2hyb21vc29tZSBvZiBpbnRlcmVzdA0KY29udHJhc3QgPC0gYygibm9ybWFsIiwgIkRLRCIpICMgU2VsZWN0IGNvbnRyYXN0DQpwYXRpZW50IDwtICJkdW1teSINCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIGMoImdlbmUiLCAiY2hyIiwgImJlZ2luIiwgImVuZCIpDQpzZWxlY3RfZ2VuZXMgPC0gcG9zaXRpb25zJGdlbmVbcG9zaXRpb25zJGNociA9PSBjaHJdICMgR3JhYiBjaHIgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb24NCg0KZmlsdGVyX2NvdW50cyA8LSBhcy5kYXRhLmZyYW1lKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImxvZ19xIl1dKQ0KY29sbmFtZXMoZmlsdGVyX2NvdW50cykgPC0gZ3N1YignLmRjYycsJycsIGNvbG5hbWVzKGZpbHRlcl9jb3VudHMpKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzWyxzZWxlY3Rfc2FtcGxlcyRTYW1wbGVfSURdICMgZmlsdGVyIG91dCBzYW1wbGVzIHRoYXQgYXJlIG5vdCB0aGUgaW50ZXJlc3RpbmcgcmVnaW9uDQoNCnNlbGVjdF9nZW5lcyA8LSBzZWxlY3RfZ2VuZXNbc2VsZWN0X2dlbmVzICVpbiUgcm93bmFtZXMoZmlsdGVyX2NvdW50cyldICMgT25seSB1c2UgZ2VuZXMgdGhhdCBhcmUgYWN0dWFsbHkgaW4gdGhlIGRhdGEgKHBrYyBnZW5lIGZpbGUgaGFzIGFsbCBvZiB0aGVtKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzW3NlbGVjdF9nZW5lcyxdICMgZmlsdGVyIG91dCBub24gY2hyb21vc29tZSBzcGVjaWZpYyBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IGNociwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTAgfQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCSCINCmNvdW50ZXI9MQ0KDQpsb2dfcV9maWx0ZXIgPC1hcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpDQoNCmNvbXBzX2RmPC1kYXRhLmZyYW1lKGNvbXA9JycsdmFsPScnKQ0KDQpmb3IgKGFjdGl2ZV9ncm91cDEgaW4gY29udHJhc3QpIHsNCiAgICBmb3IgKGFjdGl2ZV9ncm91cDIgaW4gY29udHJhc3QpIHsNCg0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgICNwcmludChjb21wKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQoNCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMl1dDQoNCiAgICAgICNydW4gdF90ZXN0cw0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCg0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQoNCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZSggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkgKSApICkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCg0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCg0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRhZGpfcF92YWx1ZSA8IDAuMDVdIDwtICJQIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KICAgICAgcmVzdWx0cyRDb2xvclthYnMocmVzdWx0cyRGQykgPCAwLjVdIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuNSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCiAgICAgICN2dWxjYW5vcGxvdA0KDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCg0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVtvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIHBsb3RzW1tjb3VudGVyXV08LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgaW4iLCBjaHIsICItIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCg0KZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLG5jb2w9MikNCmBgYA0KDQojIHQtdGVzdCBvbiB0d28gc3ViZ3JvdXBzDQoNClNlbGVjdCBub2RlcyBmcm9tIHRoZSBkZW5kcm9ncmFtIGFzIHN1Ymdyb3VwcywgaW4gdGhpcyBjYXNlIG5vZGVzIDE4IGFuZCAxNS4gRGVjaWRlIG9uIHRoZSBpbnZvbHZlZCBjaHJvbW9zb21lcyBhbmQgYWRkIHRoZW0gaW50byB0aGUgY2hyX2xpc3QuIFNwZWNpZnkgdGhlIHBhdGllbnQgaW4gcGF0aWVudC4NCg0KYGBge3J9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCmNocl9saXN0IDwtIGMoImNocjIyIikgIyBDaG9vc2UgbmVlZGVkIGNocm9tb3NvbWVzIG9mIHRoZSB0YXJnZXQgYXJlYS4gVGhpbmsgb2YgaXQgYXMgdGhlIHgtYXhpcw0KcGF0aWVudCA8LSAiZHVtbXkiDQpub2RlX2ludGVyZXN0IDwtIDIzMyAjIENob29zZSBzdWJncm91cCAxDQpub2RlX2ludGVyZXN0MiA8LSAyMTYgIyBDaG9vc2Ugc3ViZ3JvdXAgMg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnRyZWUgPC0gcmVhZC50cmVlKHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zX2RlbmRyb2dyYW0udHh0Iiwgc2VwID0gIiIpKQ0KdHJlZV9pbmZvIDwtIHRyZWUgJT4lIGFzLnRyZWVkYXRhICU+JSBhc190aWJibGUgIyB0cmFuc2Zvcm0gdHJlZSBpbnRvIGFjY2Vzc2libGUgZGF0YQ0KDQpzYW1wbGVzX2ludGVyZXN0IDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QpICMgR2V0IGxhYmVscyBhdHRhY2hlZCB0byB0aGUgZ3JvdXANCnNhbXBsZXNfaW50ZXJlc3QgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdCRsYWJlbCkpICMgZm9ybWF0dGluZw0KcGFzdGUoIlN1Ymdyb3VwIDE6ICIsIHNhbXBsZXNfaW50ZXJlc3QpDQoNCnNhbXBsZXNfaW50ZXJlc3QyIDwtIG9mZnNwcmluZyh0cmVlX2luZm8sIG5vZGVfaW50ZXJlc3QyKQ0Kc2FtcGxlc19pbnRlcmVzdDIgPC0gYXMuY2hhcmFjdGVyKG5hLm9taXQoc2FtcGxlc19pbnRlcmVzdDIkbGFiZWwpKQ0KcGFzdGUoIlN1Ymdyb3VwIDI6ICIsIHNhbXBsZXNfaW50ZXJlc3QyKQ0KYGBgDQoNCmBgYHtyfQ0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKQ0KY29sbmFtZXMocG9zaXRpb25zKSA8LSBjKCJnZW5lIiwgImNociIsICJiZWdpbiIsICJlbmQiKSAjIEZvcm1hdHRpbmcNCg0Kc2VsZWN0X2dlbmVzIDwtIHBvc2l0aW9ucyRnZW5lW3Bvc2l0aW9ucyRjaHIgJWluJSBjaHJfbGlzdF0gIyB8IHBvc2l0aW9ucyRjaHIgPT0gY2hyMl0gIyBHcmFiIGNocm9tb3NvbWUgc3BlY2lmaWMgZ2VuZXMNCg0KYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuZGVsaW0ocGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksIGhlYWRlcj1GQUxTRSkpDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBjKCJTYW1wbGVfSUQiLCAiQU5OIikNCnNlbGVjdF9zYW1wbGVzIDwtIGFubm90YXRpb25bYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0IHwgYW5ub3RhdGlvbiRTYW1wbGVfSUQgJWluJSBzYW1wbGVzX2ludGVyZXN0MixdICMgR3JhYiBzdWJncm91cCBzcGVjaWZpYyBTYW1wbGUgSURzDQoNCmZpbHRlcl9jb3VudHMgPC0gdGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sibG9nX3EiXV0NCmNvbG5hbWVzKGZpbHRlcl9jb3VudHMpIDwtIGdzdWIoJy5kY2MnLCcnLCBjb2xuYW1lcyhmaWx0ZXJfY291bnRzKSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1ssc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEXSAjIEZpbHRlciBTYW1wbGUgSUQncw0KDQpzZWxlY3RfZ2VuZXMgPC0gc2VsZWN0X2dlbmVzW3NlbGVjdF9nZW5lcyAlaW4lIHJvd25hbWVzKGZpbHRlcl9jb3VudHMpXSAjIE9ubHkgdXNlIGdlbmVzIHRoYXQgYXJlIGFjdHVhbGx5IGluIHRoZSBkYXRhIChwa2MgZ2VuZSBmaWxlIGhhcyBhbGwgb2YgdGhlbSkNCmZpbHRlcl9jb3VudHMgPC0gZmlsdGVyX2NvdW50c1tzZWxlY3RfZ2VuZXMsXSAjIEZpbHRlciBnZW5lcw0KYGBgDQoNCmBgYHtyIHR0ZXN0IHN1Ymdyb3VwcywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQpwbG90czwtbGlzdCgpDQp0YWJsZXM8LWxpc3QoKQ0KbGFiZWxzPC1saXN0KCkNCnRlc3Q8LSJ0dGVzdCINCm10YzwtIkJIIg0KY291bnRlcj0xDQoNCmxvZ19xX2ZpbHRlciA8LWFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykNCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCg0KYWN0aXZlX2dyb3VwMSA8LSBzYW1wbGVzX2ludGVyZXN0ICNzdWJncm91cCAxDQphY3RpdmVfZ3JvdXAyIDwtIHNhbXBsZXNfaW50ZXJlc3QyICNzdWJncm91cCAyDQoNCiMgZm9yIChhY3RpdmVfZ3JvdXAxIGluIGMoInN1YjEiKSkgew0KIyAgICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIGMoInN1YjIiKSkgew0KDQogICAgICAjc3VwcmVzcyByZWR1bmNhbnQgY29tcGFyZXMNCiAgICAgICNpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgICNjb21wPC1wYXN0ZShzb3J0KGMoYWN0aXZlX2dyb3VwMSxhY3RpdmVfZ3JvdXAyKSksY29sbGFwc2UgPSAiXyIpDQogICAgICAjcHJpbnQoY29tcCkNCiAgICAgICNpZihjb21wICVpbiUgY29tcHNfZGYkY29tcCkge25leHR9DQogICAgICB0ZW1wX2RmPC1kYXRhLmZyYW1lKGNvbXA9Y29tcCAsdmFsPTEpDQogICAgICBjb21wc19kZjwtcmJpbmQoY29tcHNfZGYsdGVtcF9kZikNCg0KICAgICAgIyBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICAjIGdyb3VwMTwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDFdXQ0KICAgICAgIyBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkQU5OPT1hY3RpdmVfZ3JvdXAyXV0NCg0KICAgICAgbGFiZWxzW1tjb3VudGVyXV08LXBhc3RlKGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKQ0KICAgICAgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDFdXQ0KICAgICAgZ3JvdXAyPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRCAlaW4lIGFjdGl2ZV9ncm91cDJdXQ0KDQogICAgICAjcnVuIHRfdGVzdHMNCiAgICAgIHJlc3VsdHM8LWFzLmRhdGEuZnJhbWUgKCBhcHBseShsb2dfcV9maWx0ZXIsIDEsIGZ1bmN0aW9uKHgpIHQudGVzdCh4W2NvbG5hbWVzKGdyb3VwMSldLHhbY29sbmFtZXMoZ3JvdXAyKV0pJHAudmFsdWUpICkNCiAgICAgIGNvbG5hbWVzKHJlc3VsdHMpPC0icmF3X3BfdmFsdWUiDQoNCiAgICAgICNtdWx0aXBsZV90ZXN0aW5nX2NvcnJlY3Rpb24NCiAgICAgIGFkal9wX3ZhbHVlPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9bXRjKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxhZGpfcF92YWx1ZSkNCg0KICAgICAgI2NhbGNfZmRyDQogICAgICBGRFI8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD0iZmRyIikNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsRkRSKQ0KDQogICAgICAjZm9sZF9jaGFuZ2VzDQogICAgICAjYXMgYmFzZSBkYXRhIGlzIGFscmVhZHkgbG9nIHRyYW5zZm9ybWVkLCBtZWFucyBuZWVkIHRvIGJlIHN1YnRyYWN0ZWQgdG8gZ2V0IEZDIGluIGxvZyBzcGFjZQ0KICAgICAgZmNoYW5nZXM8LWFzLmRhdGEuZnJhbWUoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgKG1lYW4oeFtjb2xuYW1lcyhncm91cDEpXSkgLSBtZWFuKHhbY29sbmFtZXMoZ3JvdXAyKV0pICkgKSApDQogICAgICBjb2xuYW1lcyhmY2hhbmdlcyk8LSJGQyINCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsZmNoYW5nZXMpDQoNCiAgICAgICNhZGQgZ2VuZW5hbWVzDQogICAgICByZXN1bHRzJEdlbmU8LXJvd25hbWVzKHJlc3VsdHMpDQoNCiAgICAgICNzZXQgY2F0ZWdvcmllcyBiYXNlZCBvbiBQLXZhbHVlICYgRkRSIGZvciBwbG90dGluZw0KICAgICAgcmVzdWx0cyRDb2xvciA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkYWRqX3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwgMC41XSA8LSAiTlMgb3IgRkMgPCAwLjUiDQogICAgICByZXN1bHRzJENvbG9yIDwtIGZhY3RvcihyZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjUiLCAiUCA8IDAuMDUiLCAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQogICAgICAjdnVsY2Fub3Bsb3QNCg0KICAgICAgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAgICAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQoNCiAgICAgIHJlc3VsdHMkaW52ZXJ0X1AgPC0gKC1sb2cxMChyZXN1bHRzJGFkal9wX3ZhbHVlKSkgKiBzaWduKHJlc3VsdHMkRkMpDQogICAgICB0b3BfZyA8LSBjKCkNCiAgICAgIHRvcF9nIDwtIGModG9wX2csDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbaW5kLCAnR2VuZSddWw0KICAgICAgICAgICAgICAgICAgIG9yZGVyKHJlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gRkFMU0UpWzE6MTVdXSkNCiAgICAgIHRvcF9nPC0gdW5pcXVlKHRvcF9nKQ0KICAgICAgcmVzdWx0cyA8LSByZXN1bHRzWywgLTEqbmNvbChyZXN1bHRzKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCg0KICAgICAgIyBHcmFwaCByZXN1bHRzDQogICAgICAjcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgcCA8LSBnZ3Bsb3QocmVzdWx0cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBGQywgeSA9IC1sb2cxMChhZGpfcF92YWx1ZSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoMC41LCAtMC41KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICBsYWJzKHggPSBwYXN0ZSgiRW5yaWNoZWQgZ2VuZXMgZnJvbSIsIGNocl9saXN0LCAiaW4iLCAic3ViZ3JvdXAgMiIsIiA8LSBsb2cyKEZDKSAtPiBFbnJpY2hlZCBpbiIsICJzdWJncm91cCAxIiksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCBGRFI8MC4wNSAmICgtMC41PkZDfCBGQz4wLjUpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjE1LCBjb2xvciA9ICJibGFjayIsIHNpemU9My41LA0KICAgICAgICAgICAgICAgICAgICAgICAgbWluLnNlZ21lbnQubGVuZ3RoID0gLjEsIGJveC5wYWRkaW5nID0gLjIsIGx3ZCA9IDIsDQogICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMCkgKw0KICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICAgICAgICAjZ2d0aXRsZShwYXN0ZShzbGlkZSwiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQogICAgICAgIGdndGl0bGUocGFzdGUocGF0aWVudCwgIjogIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgIyAgIH0NCiAgIyB9DQoNCiNnZ3Bsb3RseShwKQ0KcA0KI2dyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KIyAxMiBDb2RlICYgVmVyc2lvbnMNCg0KUGlwZWxpbmV2ZXJzaW9uOiB2MSBiYXNlZCBvbjoNCjxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvd29ya2Zsb3dzL3ZpZ25ldHRlcy9HZW9NeFdvcmtmbG93cy9pbnN0L2RvYy9HZW9teFRvb2xzX1JOQS1OR1NfQW5hbHlzaXMuaHRtbD4NCg0KVGhlIHVuZGVybHlpbmcgY29kZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSAnQ29kZScsIGJ1dHRvbiBvbiB0aGUgdG9wDQpvZiB0aGlzIHBhZ2UuIENob29zZSBvcHRpb24gJ2Rvd25sb2FkIFJtZCcgdG8gZG93bmxvYWQgdGhlIGZ1bGwgcGlwZWxpbmUNCndoaWNoIGNhbiBiZSBvcGVuZWQgaW4gUiBvciBSc3R1ZGlvLiBTb21lIGZpbGVwYXRocyBhcmUgaGFyZGNvZGVkIGFuZA0KbmVlZCB0byBiZSBjaGFuZ2VkIGFjY29yZGluZyB0byB5b3VyIHNldHVwLg0KDQojIDEyLjEgUiBzZXNzaW9uIGluZm9ybWF0aW9uDQoNCmBgYHtyIHNlc3Npb25faW5mb30NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQojIyAxMi4yIFJlZmVyZW5jZXMNCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL2RlY29yYXRpb24tc3Ryb2tlLWZsYXQucG5nKQ0KDQpgYGB7cn0NCmtuaXRyOjprbml0X2V4aXQoKQ0KYGBgDQoNCiMgOS4wIHJlbG9hZCBkYXRhDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fcHJlcGFyZX0NCiNyZWxvYWQgZGF0YQ0KIyBEYXRhIDwtDQojICAgcmVhZE5hbm9TdHJpbmdHZW9NeFNldChkY2NGaWxlcyA9IERDQ0ZpbGVzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGtjRmlsZXMgPSBQS0NGaWxlcywNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YVNoZWV0ID0gIlNoZWV0MSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFEY2NDb2xOYW1lID0gIlNhbXBsZV9JRCIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwcm90b2NvbERhdGFDb2xOYW1lcyA9IGMoImFvaSIsICJyb2kiKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KIyANCiMgcGtjcyA8LSBhbm5vdGF0aW9uKERhdGEpDQojIG1vZHVsZXMgPC0gZ3N1YigiLnBrYyIsICIiLCBwa2NzKQ0KIyANCiMgI3NoaWZ0IGFueSBleHByZXNzaW9uIGNvdW50cyB3aXRoIGEgdmFsdWUgb2YgMCB0byAxIHRvIGVuYWJsZSBpbiBkb3duc3RyZWFtIHRyYW5zZm9ybWF0aW9ucy4NCiMgRGF0YSA8LSBzaGlmdENvdW50c09uZShEYXRhLCB1c2VEQUxvZ2ljID0gVFJVRSkNCiMgDQojICNjb2xsYXBzX3RhcmdldHMNCiMgdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQojIGRpbSh0YXJnZXRfRGF0YSkNCiMgDQojICNub3JtYWxpemUNCiMgdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwgZGF0YV90eXBlID0gIlJOQSIsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpcmVkUXVhbnRpbGUgPSAuNzUsDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9FbHQgPSAicV9ub3JtIikNCmBgYA0KDQojIDkuNCBSdW4gYWR2YW5jZWQgc3BhdGlhbCBkZXZvbmNvbHV0aW9uDQoNCmBgYHtyfQ0KIyB2ZWN0b3IgaWRlbnRpZnlpbmcgcHVyZSB0dW1vciBzZWdtZW50czoNCnRhcmdldF9EYXRhJGlzdHVtb3IgPSB0YXJnZXRfRGF0YSRBTk4yID09ICJFcGl0aGVsaXVtIg0KDQojIHJ1biBzcGF0aWFsZGVjb24gd2l0aCBhbGwgdGhlIGJlbGxzIGFuZCB3aGlzdGxlczoNCnJlc3RpbHMgPSBydW5zcGF0aWFsZGVjb24ob2JqZWN0ID0gdGFyZ2V0X0RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZWx0ID0gInFfbm9ybSIsICAgICAgICAgICAgICAgICAgICAjIG5vcm1hbGl6ZWQgZGF0YQ0KICAgICAgICAgICAgICAgICAgICAgICAgICByYXdfZWx0ID0gImV4cHJzIiwgICAgICAgICAgICAgICAgICAgICAgIyBleHBlY3RlZCBiYWNrZ3JvdW5kIGNvdW50cyBmb3IgZXZlcnkgZGF0YSBwb2ludCBpbiBub3JtDQogICAgICAgICAgICAgICAgICAgICAgICAgIFggPSBzYWZlVE1FLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHNhZmVUTUUgbWF0cml4LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbG1lcmdlcyA9IHNhZmVUTUUubWF0Y2hlcywgICAgICAgICAgICMgc2FmZVRNRS5tYXRjaGVzIG9iamVjdCwgdXNlZCBieSBkZWZhdWx0DQogICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfY291bnRzID0gdGFyZ2V0X0RhdGEkbnVjbGVpLCAgICAgICMgbnVjbGVpIGNvdW50cywgdXNlZCB0byBlc3RpbWF0ZSB0b3RhbCBjZWxscw0KICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wdXJlX3R1bW9yID0gdGFyZ2V0X0RhdGEkaXN0dW1vciwgICAjIGlkZW50aXRpZXMgb2YgdGhlIFR1bW9yIHNlZ21lbnRzL29ic2VydmF0aW9ucw0KICAgICAgICAgICAgICAgICAgICAgICAgICBuX3R1bW9yX2NsdXN0ZXJzID0gNSkgICAgICAgICAgICAgICAgICAgIyBob3cgbWFueSBkaXN0aW5jdCB0dW1vciBwcm9maWxlcyB0byBhcHBlbmQgdG8gc2FmZVRNRQ0KDQojc3RyKHBEYXRhKHJlc3RpbHMpKQ0KaGVhdG1hcChzd2VlcChyZXN0aWxzQGV4cGVyaW1lbnREYXRhQG90aGVyJFNwYXRpYWxEZWNvbk1hdHJpeCwgMSwgYXBwbHkocmVzdGlsc0BleHBlcmltZW50RGF0YUBvdGhlciRTcGF0aWFsRGVjb25NYXRyaXgsIDEsIG1heCksICIvIiksDQogICAgICAgICBsYWJSb3cgPSBOQSwgbWFyZ2lucyA9IGMoMTAsIDUpKQ0KDQpgYGANCg0KPCEtLSAjIDkuMy4yIFBsb3R0aW5nIGRlY29udm9sdXRpb24gcmVzdWx0cyAtLT4NCg0KPCEtLSBgYGB7ciwgZmlnLndpZHRoPTE1LGZpZy5oZWlnaHQ9N30gLS0+DQoNCjwhLS0gIyBGb3IgcmVmZXJlbmNlLCBzaG93IHRoZSBUSUxzIGNvbG9yIGRhdGEgb2JqZWN0IHVzZWQgYnkgdGhlIHBsb3R0aW5nIGZ1bmN0aW9ucyAgLS0+DQoNCjwhLS0gIyB3aGVuIHNhZmVUTUUgaGFzIGJlZW4gdXNlZDogLS0+DQoNCjwhLS0gZGF0YSgiY2VsbGNvbHMiKSAtLT4NCg0KPCEtLSAjY2VsbGNvbHMgLS0+DQoNCjwhLS0gbyA9IGhjbHVzdChkaXN0KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzKSkpJG9yZGVyIC0tPg0KDQo8IS0tIGxheW91dChtYXRyaXgoYygxLCAyKSwgMSksIHdpZHRocyA9IGMoNywgMykpIC0tPg0KDQo8IS0tIFRJTF9iYXJwbG90KHQocmVzJGNlbGwuY291bnRzJGNlbGwuY291bnRzWywgb10pLCAtLT4NCg0KPCEtLSAgICAgICAgICAgICBob3JpeiA9IFRSVUUsIGRyYXdfbGVnZW5kID0gVFJVRSwgY2V4Lm5hbWVzID0gMC45KSAtLT4NCg0KPCEtLSAjcGFyKG1hcj1jKDIsIDE1LCAyLCAyKSkgLS0+DQoNCjwhLS0gIyBvciB0aGUgcHJvcG9ydGlvbnMgb2YgY2VsbHM6IC0tPg0KDQo8IS0tIHRlbXAgPSByZXBsYWNlKHJlcyRwcm9wX29mX25vbnR1bW9yLCBpcy5uYShyZXMkcHJvcF9vZl9ub250dW1vciksIDApIC0tPg0KDQo8IS0tIG8gPSBoY2x1c3QoZGlzdCh0ZW1wW3JlcyRBTk4yID09ICJDRDQ1IixdKSkkb3JkZXIgLS0+DQoNCjwhLS0gVElMX2JhcnBsb3QodChyZXMkcHJvcF9vZl9hbGwpLCAgLS0+DQoNCjwhLS0gICAgICAgICAgICAgaG9yaXogPSBUUlVFLCBkcmF3X2xlZ2VuZCA9IFRSVUUsIGNleC5uYW1lcyA9IDAuOSkgLS0+DQoNCjwhLS0gYGBgIC0tPg0KDQojIDkuMy4zIEZsb3JldHMgb2YgU3BhdGlhbCBkZWNvbnZvbHV0aW9uDQoNClRoZSBzZWNvbmQgZnVuY3Rpb24gaXMgImZsb3JldHMiLCB1c2VkIGZvciBwbG90dGluZyBjZWxsIGFidW5kYW5jZXMgYXRvcA0Kc29tZSAyLUQgcHJvamVjdGlvbi4gSGVyZSwgd2UnbGwgcGxvdCBjZWxsIGFidW5kYW5jZXMgYXRvcCB0aGUgZmlyc3QgMg0KcHJpbmNpcGFsIGNvbXBvbmVudHMgb2YgdGhlIGRhdGE6DQoNCmBgYHtyfQ0KeHkgPC0gcmVhZF9leGNlbCgiTDovcGtsb29zdGVybWFuL0tpZG5leV9vcmdhbl9hdGxhcy9hbm5vdGF0aW9uL09yZ2FuQXRsYXNfS2lkbmV5Lnhsc3giLCBzaGVldCA9ICJTZWdtZW50UHJvcGVydGllcyIpDQp4eSRuZXd4IDwtICh4eSRST0lDb29yZGluYXRlWCAtIHh5JFNjYW5PZmZzZXRYKQ0KeHkkbmV3eSA8LSAoeHkkUk9JQ29vcmRpbmF0ZVkgLSB4eSRTY2FuT2Zmc2V0WSkNCnh5IDwtIHh5WyxjKCJTZWdtZW50RGlzcGxheU5hbWUiLCAibmV3eCIsICJuZXd5IildDQp4eVsiYW9pIl0gPC0geHlbIlNlZ21lbnREaXNwbGF5TmFtZSJdDQphbm5vdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YQ0KYW5ub3QgPC0gYW5ub3QgJT4lIGlubmVyX2pvaW4oeHksIGJ5ID0gJ2FvaScpICMlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCmFubm90JHggPC0gYW5ub3QkbmV3eA0KYW5ub3QkeSA8LSBhbm5vdCRuZXd5DQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD0yNSxmaWcuaGVpZ2h0PTE1LCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjUwJSJ9DQojUDM2ID0gY3JlYXRlUGFsZXR0ZShsZW5ndGgocm93bmFtZXModChyZXMkYmV0YSkpKSwgIGMoIiNmZjAwMDAiLCAiIzAwZmYwMCIsICIjMDAwMGZmIikpDQoNCnhsaW0gPC0gbWF4KHJlc0BwaGVub0RhdGFAZGF0YSR4KQ0KeWxpbSA8LSBtYXgocmVzQHBoZW5vRGF0YUBkYXRhJHkpDQoNCmluZCA8LSBwRGF0YShyZXMpJHNsaWRlX25hbWUgPT0gImh1X2tpZG5leV8wMDEiDQpyZXMyIDwtIHJlc1ssaW5kXQ0KDQojYSA8LSByZXMyWyFyZXMyQHBoZW5vRGF0YUBkYXRhW1siYmV0YSJdXT09MF0NCg0KI3NsaWRlX2xpc3RbWzRdXSA8LSBmbGlwWShzbGlkZV9saXN0W1s0XV0pDQoNCmZsdW9yIDwtIGZsdW9yTGVnZW5kKG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIG5yb3cgPSA0LCB0ZXh0U2l6ZSA9IDUsIGFscGhhID0gMC4yNSkNCg0KaW0gPC0gcGxvdFNwYXRpYWxPdmVybGF5KG92ZXJsYXkgPSBzbGlkZV9saXN0W1s0XV0sIGxlZ2VuZCA9IEZBTFNFLCBoaVJlcyA9IEZBTFNFLCBjb3JuZXIgPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgICAgICAgICBzY2FsZUJhcldpZHRoID0gMC41LCB0ZXh0RGlzdGFuY2UgPSA1LCBzY2FsZUJhckNvbG9yID0gIndoaXRlIiwgaW1hZ2UgPSBUUlVFKSArIA0KICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gcGFzdGUoIldob2xlIHNsaWRlIE9NRS1USUZGIiwgc2xpZGVOYW1lKHNsaWRlX2xpc3RbWzRdXSkpKQ0KDQojcGxvdCgwLCB4bGltID0gYygwLCB4bGltKzQwMDAwKSwgeWxpbSA9IGMoMCwgeWxpbSksIGF4ZXMgPSBGQUxTRSkNCg0KY293cGxvdDo6Z2dkcmF3KCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoaW0sIHggPSAtMC4zLCB5ID0gMCkgKw0KICBjb3dwbG90OjpkcmF3X3Bsb3QoZmx1b3IsIHNjYWxlID0gMC4xMiwgeCA9IDAuMSwgeSA9IDApICMrICMsIHNjYWxlID0gMC4xMiwgeCA9IC0wLjMsIHkgPSAtMC4yNSkNCg0KDQpmbG9yZXRzKA0KICB4ID0gcmVzMkBwaGVub0RhdGFAZGF0YSR4LCMgKyA0MDAwMCwNCiAgeSA9IHJlczJAcGhlbm9EYXRhQGRhdGEkeSwNCiAgY29sID0gUDM2LA0KICAjY29sID0gMTpsZW5ndGgocm93bmFtZXModChhJGJldGEpKSksDQogIHhsaW0gPSBjKDAseGxpbSs0MDAwMCksDQogIHlsaW0gPSBjKDAseWxpbSksDQogIGIgPSB0KGEkYmV0YSksDQogIHJlc2NhbGUuYnkuc3FydCA9IFRSVUUsDQogIGFkZCA9IEZBTFNFLA0KICBjZXggPSAxLCB4bGFiID0gIiIsIHlsYWIgPSAiIikNCg0KbGVnZW5kKCJyaWdodCIsDQogICAgICAgI2ZpbGwgPSAxOmxlbmd0aChyb3duYW1lcyh0KGEkYmV0YSkpKSwNCiAgICAgICBmaWxsID0gUDM2LA0KICAgICAgIGxlZ2VuZCA9IHJvd25hbWVzKHQoYSRiZXRhKSksIGNleCA9IDEuNSkNCmBgYA0KDQojIDkuNCBjb21iaW5pbmcgY2VsbHR5cGVzDQoNCldoZW4gdHdvIGNlbGwgdHlwZXMgYXJlIHRvbyBzaW1pbGFyLCB0aGUgZXN0aW1hdGlvbiBvZiB0aGVpciBhYnVuZGFuY2VzDQpiZWNvbWVzIHVuc3RhYmxlLiBIb3dldmVyLCB0aGVpciBzdW0gY2FuIHN0aWxsIGJlIGVzdGltYXRlZCBlYXNpbHkuIFRoZQ0KZnVuY3Rpb24gImNvbGxhcHNlQ2VsbFR5cGVzIiB0YWtlcyBhIGRlY29udm9sdXRpb24gcmVzdWx0cyBvYmplY3QgYW5kDQpjb2xsYXBzZXMgYW55IGNvbHNlbHktcmVsYXRlZCBjZWxsIHR5cGVzIHlvdSB0ZWxsIGl0IHRvDQoNCmBgYHtyfQ0KbWF0Y2hpbmcgPSBsaXN0KCkNCm1hdGNoaW5nJG15ZWxvaWQgPSBjKCAibWFjcm9waGFnZXMiLCAibW9ub2N5dGVzIiwgIm1EQ3MiKQ0KbWF0Y2hpbmckVC5OSyA9IGMoIkNENC5ULmNlbGxzIiwiQ0Q4LlQuY2VsbHMiLCAiVHJlZyIsICJOSyIpDQptYXRjaGluZyRCID0gYygiQiIpDQptYXRjaGluZyRtYXN0ID0gYygibWFzdCIpDQptYXRjaGluZyRuZXV0cm9waGlscyA9IGMoIm5ldXRyb3BoaWxzIikNCm1hdGNoaW5nJHN0cm9tYSA9IGMoImVuZG90aGVsaWFsLmNlbGxzIiwgImZpYnJvYmxhc3RzIikNCg0KY29sbGFwc2VkID0gY29sbGFwc2VDZWxsVHlwZXMoZml0ID0gcmVzdGlscywgbWF0Y2hpbmcgPSBtYXRjaGluZykNCg0KaGVhdG1hcChjb2xsYXBzZWQkYmV0YSwgY2V4Um93ID0gMC44NSwgY2V4Q29sID0gMC43NSkNCmBgYA0KDQojIExpZ2FuZC1yZWNlcHRvciBhbmFseXNlDQoNCmBgYHtyfQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1Yigic2FleXNsYWIvbmljaGVuZXRyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCdjaXJjbGl6ZScpDQpsaWJyYXJ5KG5pY2hlbmV0cikNCmxpYnJhcnkoU2V1cmF0KQ0KbGlicmFyeShjaXJjbGl6ZSkNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIEdldCBjb3VudHMgZGF0YQ0KY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKQ0KY291bnRzJFggPC0gcm93bmFtZXMoY291bnRzKQ0KI3Jvd25hbWVzKGNvdW50cykgPC0gTlVMTA0KI2NvdW50cyA8LSBhcy5tYXRyaXgoY291bnRzKQ0KDQojIEdldCBtZXRhIGRhdGENCm1ldGEgPC0gcERhdGEodGFyZ2V0X0RhdGEpDQptZXRhIDwtIG1ldGFbYygiQU5OMSIsICJBTk4yIiwgInNsaWRlX25hbWUiKV0NCm1ldGEkU2FtcGxlSUQgPC0gcHJvdG9jb2xEYXRhKHRhcmdldF9EYXRhKVtbIlNhbXBsZUlEIl1dDQptZXRhJHJvaSA8LSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpW1sicm9pIl1dDQoNCiMgY3JlYXRlIFNldXJhdCBmb3IgYmV0dGVyIG5pY2hlIGNvb3BlcmF0aW9uDQpTZXVyYXQuRGF0YSA8LSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzID0gY291bnRzLCBtZXRhLmRhdGEgPSBtZXRhKSANCg0KIyMgQ2hlY2sgbWV0YSBkYXRhDQojU2V1cmF0LkRhdGFAbWV0YS5kYXRhICU+JSBoZWFkKCkNCiNTZXVyYXQuRGF0YUBtZXRhLmRhdGEkUmVnaW9uICU+JSB0YWJsZSgpIA0KDQojIyBDaGFuZ2UgaWRlbnRzIHRvIGNvcnJlY3QgYW5ub3RhdGlvbg0KU2V1cmF0LkRhdGEgPC0gU2V0SWRlbnQoU2V1cmF0LkRhdGEsIHZhbHVlID0gIkFOTjEiKQ0KI1NldXJhdC5EYXRhQGFjdGl2ZS5pZGVudA0KDQojIyMjIyMjIFJlYWQgaW4gbGlnYW5kLXRhcmdldCBwcmlvciBtb2RlbCwgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgYW5kIHdlaWdodGVkIGludGVncmF0ZWQgbmV0d29ya3MNCmxpZ2FuZF90YXJnZXRfbWF0cml4ID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy9saWdhbmRfdGFyZ2V0X21hdHJpeC5yZHMiKSkNCiNsaWdhbmRfdGFyZ2V0X21hdHJpeFsxOjUsMTo1XSAjIHRhcmdldCBnZW5lcyBpbiByb3dzLCBsaWdhbmRzIGluIGNvbHVtbnMNCg0KbHJfbmV0d29yayA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvbHJfbmV0d29yay5yZHMiKSkNCiNoZWFkKGxyX25ldHdvcmspDQoNCndlaWdodGVkX25ldHdvcmtzID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy93ZWlnaHRlZF9uZXR3b3Jrcy5yZHMiKSkNCndlaWdodGVkX25ldHdvcmtzX2xyID0gd2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnICU+JSBpbm5lcl9qb2luKGxyX25ldHdvcmsgJT4lIGRpc3RpbmN0KGZyb20sdG8pLCBieSA9IGMoImZyb20iLCJ0byIpKQ0KI2hlYWQod2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnKSAjIGludGVyYWN0aW9ucyBhbmQgdGhlaXIgd2VpZ2h0cyBpbiB0aGUgbGlnYW5kLXJlY2VwdG9yICsgc2lnbmFsaW5nIG5ldHdvcmsNCg0KDQojIyMjIERlZmluZSBhIHNlbmRlciBhbmQgcmVjZWl2ZXIgcG9wdWxhdGlvbiBhbmQgZGV0ZXJtaW5lIHdoaWNoIGdlbmVzIGFyZSBleHByZXNzZWQgaW4gYm90aCBwb3B1bGF0aW9ucw0KDQojIyByZWNlaXZlcg0KcmVjZWl2ZXIgPSBjKCJDRDEwKyIpDQpleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIgPSBnZXRfZXhwcmVzc2VkX2dlbmVzKHJlY2VpdmVyLCBTZXVyYXQuRGF0YSwgcGN0ID0gMC4xMCkNCmJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gZXhwcmVzc2VkX2dlbmVzX3JlY2VpdmVyICU+JSAuWy4gJWluJSByb3duYW1lcyhsaWdhbmRfdGFyZ2V0X21hdHJpeCldDQoNCiMjIHNlbmRlcg0Kc2VuZGVyX2NlbGx0eXBlcyA9IGMoIlBhbkNLKyIpDQpsaXN0X2V4cHJlc3NlZF9nZW5lc19zZW5kZXIgPSBzZW5kZXJfY2VsbHR5cGVzICU+JSB1bmlxdWUoKSAlPiUgbGFwcGx5KGdldF9leHByZXNzZWRfZ2VuZXMsIFNldXJhdC5EYXRhLCAwLjEwKSAjIGxhcHBseSB0byBnZXQgdGhlIGV4cHJlc3NlZCBnZW5lcyBvZiBldmVyeSBzZW5kZXIgY2VsbCB0eXBlIHNlcGFyYXRlbHkgaGVyZQ0KZXhwcmVzc2VkX2dlbmVzX3NlbmRlciA9IGxpc3RfZXhwcmVzc2VkX2dlbmVzX3NlbmRlciAlPiUgdW5saXN0KCkgJT4lIHVuaXF1ZSgpDQoNCiMjIyMgSW1wb3J0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpbiByZWNpZXZlciBwb3B1bGF0aW9ucw0KZ2VuZXNldC5vaSA8LSB0b3BfZw0KDQojIyMjIyAgRGVmaW5lIGEgc2V0IG9mIGxpZ2FuZHMgaW4gc2VuZGVyIHBvcHVsYXRpb25zDQpsaWdhbmRzID0gbHJfbmV0d29yayAlPiUgcHVsbChmcm9tKSAlPiUgdW5pcXVlKCkNCnJlY2VwdG9ycyA9IGxyX25ldHdvcmsgJT4lIHB1bGwodG8pICU+JSB1bmlxdWUoKQ0KDQpleHByZXNzZWRfbGlnYW5kcyA9IGludGVyc2VjdChsaWdhbmRzLGV4cHJlc3NlZF9nZW5lc19zZW5kZXIpDQpleHByZXNzZWRfcmVjZXB0b3JzID0gaW50ZXJzZWN0KHJlY2VwdG9ycyxleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIpDQoNCnBvdGVudGlhbF9saWdhbmRzID0gbHJfbmV0d29yayAlPiUgZmlsdGVyKGZyb20gJWluJSBleHByZXNzZWRfbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIHB1bGwoZnJvbSkgJT4lIHVuaXF1ZSgpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIFBlcmZvcm0gTmljaGVuZXQgYW5hbHlzaXMNCg0KDQpsaWdhbmRfYWN0aXZpdGllcyA9IHByZWRpY3RfbGlnYW5kX2FjdGl2aXRpZXMoZ2VuZXNldCA9IGdlbmVzZXQub2ksIGJhY2tncm91bmRfZXhwcmVzc2VkX2dlbmVzID0gYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIHBvdGVudGlhbF9saWdhbmRzID0gcG90ZW50aWFsX2xpZ2FuZHMpDQoNCmxpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pIA0KI2xpZ2FuZF9hY3Rpdml0aWVzID0gbGlnYW5kX2FjdGl2aXRpZXMgJT4lIGFycmFuZ2UoLXBlYXJzb24pICU+JSBtdXRhdGUocmFuayA9IHJhbmsoZGVzYyhwZWFyc29uKSkpDQoNCg0KIyMjIyMjIyMjIyMjIyMNCiMjIyMjIyMgSGlzdG9ncmFtIG9mIGxpZ2FuZCBhY3Rpdml0aWVzDQoNCnBfaGlzdF9saWdfYWN0aXZpdHkgPSBnZ3Bsb3QobGlnYW5kX2FjdGl2aXRpZXMsIGFlcyh4PXBlYXJzb24pKSArIA0KICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0iYmxhY2siLCBmaWxsPSJkYXJrb3JhbmdlIikgICsgDQogICMgZ2VvbV9kZW5zaXR5KGFscGhhPS4xLCBmaWxsPSJvcmFuZ2UiKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWluKGxpZ2FuZF9hY3Rpdml0aWVzICU+JSB0b3BfbigyMCwgcGVhcnNvbikgJT4lIHB1bGwocGVhcnNvbikpKSwgY29sb3I9InJlZCIsIGxpbmV0eXBlPSJkYXNoZWQiLCBzaXplPTEpICsgDQogIGxhYnMoeD0ibGlnYW5kIGFjdGl2aXR5IChQQ0MpIiwgeSA9ICIjIGxpZ2FuZHMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KcF9oaXN0X2xpZ19hY3Rpdml0eQ0KDQoNCiMjIyBEb3QgYmxvdCBvZiB0b3AgbGlnYW5kcyBzcGxpdCBieSByZWdpb24NCg0KYmVzdF91cHN0cmVhbV9saWdhbmRzID0gYyhsaWdhbmRfYWN0aXZpdGllcyAlPiUgdG9wX24oMjAsIHBlYXJzb24pICU+JSBhcnJhbmdlKC1wZWFyc29uKSAlPiUgcHVsbCh0ZXN0X2xpZ2FuZCkgJT4lIHVuaXF1ZSgpKQ0KDQojIHRlc3QgPC0gU2V1cmF0LkRhdGENCiMgeWVldCA8LSBjKCJXTlQzQSIpDQojIGRhdGEoInBibWNfc21hbGwiKQ0KIyB0ZXN0W1siUk5BIl1dQHZhci5mZWF0dXJlcyA8LSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMNCiMgI2RhdGEuZnJhbWUocm93Lm5hbWVzID0gcm93bmFtZXModGVzdFtbIlJOQSJdXSkpDQojIA0KIyBEb3RQbG90KHRlc3QsIGZlYXR1cmVzID0gYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xzID0gIlJkWWxCdSIpICsgUm90YXRlZEF4aXMoKQ0KDQojIyBlbHNlDQoNCmFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmID0gYmVzdF91cHN0cmVhbV9saWdhbmRzICU+JSBsYXBwbHkoZ2V0X3dlaWdodGVkX2xpZ2FuZF90YXJnZXRfbGlua3MsZ2VuZXNldCA9IGdlbmVzZXQub2ksIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIG4gPSAyNTApICU+JSBiaW5kX3Jvd3MoKQ0KYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3MgPSBwcmVwYXJlX2xpZ2FuZF90YXJnZXRfdmlzdWFsaXphdGlvbihsaWdhbmRfdGFyZ2V0X2RmID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYsIGxpZ2FuZF90YXJnZXRfbWF0cml4ID0gbGlnYW5kX3RhcmdldF9tYXRyaXgsIGN1dG9mZiA9IDAuMjUpDQoNCm9yZGVyX2xpZ2FuZHMgPSBpbnRlcnNlY3QoYmVzdF91cHN0cmVhbV9saWdhbmRzLCBjb2xuYW1lcyhhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rcykpICU+JSByZXYoKQ0Kb3JkZXJfdGFyZ2V0cyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmJHRhcmdldCAlPiUgdW5pcXVlKCkNCnZpc19saWdhbmRfdGFyZ2V0ID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3Nbb3JkZXJfdGFyZ2V0cyxvcmRlcl9saWdhbmRzXSAlPiUgdCgpDQoNCnBfbGlnYW5kX3RhcmdldF9uZXR3b3JrID0gdmlzX2xpZ2FuZF90YXJnZXQgJT4lIG1ha2VfaGVhdG1hcF9nZ3Bsb3QoIlByaW9yaXRpemVkIFBhbkNLKyBsaWdhbmRzIiwiQ0QxMCsgcG90ZW50aWFsIHJlY2VwdG9yIGNlbGxzIiwgY29sb3IgPSAicHVycGxlIixsZWdlbmRfcG9zaXRpb24gPSAidG9wIiwgeF9heGlzX3Bvc2l0aW9uID0gInRvcCIsbGVnZW5kX3RpdGxlID0gIlJlZ3VsYXRvcnkgcG90ZW50aWFsIikgKyBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGVzbW9rZSIsICBoaWdoID0gInB1cnBsZSIsIGJyZWFrcyA9IGMoMCwwLjAwNSwwLjAxKSkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpKQ0KDQpwX2xpZ2FuZF90YXJnZXRfbmV0d29yaw0KDQojIyBjaXJjb3MNCg0KIyBRQyBvbiBxdWFudGlsZQ0KY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiR3ZWlnaHQgJT4lIHF1YW50aWxlKDAuNjYpDQphY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiAlPiUgZmlsdGVyKHdlaWdodCA+IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzKQ0KDQpsaWdhbmRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkbGlnYW5kICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJGxpZ2FuZCAlPiUgdW5pcXVlKCkpDQp0YXJnZXRzX3RvX3JlbW92ZSA9IHNldGRpZmYoYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkdGFyZ2V0ICU+JSB1bmlxdWUoKSwgYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGZfY2lyY29zJHRhcmdldCAlPiUgdW5pcXVlKCkpDQogIA0KY2lyY29zX2xpbmtzID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYgJT4lIGZpbHRlcighdGFyZ2V0ICVpbiUgdGFyZ2V0c190b19yZW1vdmUgJiFsaWdhbmQgJWluJSBsaWdhbmRzX3RvX3JlbW92ZSkNCg0KIyBDb2xvcg0KZ3JpZF9jb2xfbGlnYW5kID1jKCJQYW5DSysiID0gInJlZCINCiAgICAgICAgICAgICAgICAgICMsIkdlbmVyYWwiID0gImdyZWVuIg0KICAgICAgICAgICAgICAgICAgICkNCmdyaWRfY29sX3RhcmdldCA9YygiQ0QxMCsiID0gImdyZWVuIikNCg0KZ3JpZF9jb2xfdGJsX2xpZ2FuZCA9IHRpYmJsZShsaWdhbmRfdHlwZSA9IGdyaWRfY29sX2xpZ2FuZCAlPiUgbmFtZXMoKSwgY29sb3JfbGlnYW5kX3R5cGUgPSBncmlkX2NvbF9saWdhbmQpDQpncmlkX2NvbF90YmxfdGFyZ2V0ID0gdGliYmxlKHRhcmdldF90eXBlID0gZ3JpZF9jb2xfdGFyZ2V0ICU+JSBuYW1lcygpLCBjb2xvcl90YXJnZXRfdHlwZSA9IGdyaWRfY29sX3RhcmdldCkNCg0KY2lyY29zX2xpbmtzID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUobGlnYW5kID0gcGFzdGUobGlnYW5kLCIgIikpICMgZXh0cmEgc3BhY2U6IG1ha2UgYSBkaWZmZXJlbmNlIGJldHdlZW4gYSBnZW5lIGFzIGxpZ2FuZCBhbmQgYSBnZW5lIGFzIHRhcmdldCENCmNpcmNvc19saW5rcyA9IGNpcmNvc19saW5rcyAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfbGlnYW5kLCBieSA9IGNoYXJhY3RlcigpKSAlPiUgaW5uZXJfam9pbihncmlkX2NvbF90YmxfdGFyZ2V0LCBieSA9IGNoYXJhY3RlcigpKQ0KbGlua3NfY2lyY2xlID0gY2lyY29zX2xpbmtzICU+JSBzZWxlY3QobGlnYW5kLHRhcmdldCwgd2VpZ2h0KQ0KDQpsaWdhbmRfY29sb3IgPSBjaXJjb3NfbGlua3MgJT4lIGRpc3RpbmN0KGxpZ2FuZCxjb2xvcl9saWdhbmRfdHlwZSkNCmdyaWRfbGlnYW5kX2NvbG9yID0gbGlnYW5kX2NvbG9yJGNvbG9yX2xpZ2FuZF90eXBlICU+JSBzZXRfbmFtZXMobGlnYW5kX2NvbG9yJGxpZ2FuZCkNCnRhcmdldF9jb2xvciA9IGNpcmNvc19saW5rcyAlPiUgZGlzdGluY3QodGFyZ2V0LGNvbG9yX3RhcmdldF90eXBlKQ0KZ3JpZF90YXJnZXRfY29sb3IgPSB0YXJnZXRfY29sb3IkY29sb3JfdGFyZ2V0X3R5cGUgJT4lIHNldF9uYW1lcyh0YXJnZXRfY29sb3IkdGFyZ2V0KQ0KDQpncmlkX2NvbCA9IGMoZ3JpZF9saWdhbmRfY29sb3IsZ3JpZF90YXJnZXRfY29sb3IpDQoNCiMgZ2l2ZSB0aGUgb3B0aW9uIHRoYXQgbGlua3MgaW4gdGhlIGNpcmNvcyBwbG90IHdpbGwgYmUgdHJhbnNwYXJhbnQgfiBsaWdhbmQtdGFyZ2V0IHBvdGVudGlhbCBzY29yZQ0KdHJhbnNwYXJlbmN5ID0gY2lyY29zX2xpbmtzICU+JSBtdXRhdGUod2VpZ2h0ID0od2VpZ2h0LW1pbih3ZWlnaHQpKS8obWF4KHdlaWdodCktbWluKHdlaWdodCkpKSAlPiUgbXV0YXRlKHRyYW5zcGFyZW5jeSA9IDEtd2VpZ2h0KSAlPiUgLiR0cmFuc3BhcmVuY3kgDQoNCiMgU3BlY2lmaWMgb3JkZXIgd2l0aCBnYXBzIGlmIG5lZWRlZA0KdGFyZ2V0X29yZGVyID0gY2lyY29zX2xpbmtzJHRhcmdldCAlPiUgdW5pcXVlKCkNCiNsaWdhbmRfb3JkZXIgPSBjKENBRl9zcGVjaWZpY19saWdhbmRzLGdlbmVyYWxfbGlnYW5kcyxlbmRvdGhlbGlhbF9zcGVjaWZpY19saWdhbmRzKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCmxpZ2FuZF9vcmRlciA9IGMoKSAlPiUgYyhwYXN0ZSguLCIgIikpICU+JSBpbnRlcnNlY3QoY2lyY29zX2xpbmtzJGxpZ2FuZCkNCm9yZGVyID0gYyhsaWdhbmRfb3JkZXIsdGFyZ2V0X29yZGVyKQ0KDQp3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSA9IDAuNQ0Kd2lkdGhfZGlmZmVyZW50X2NlbGwgPSA2DQp3aWR0aF9saWdhbmRfdGFyZ2V0ID0gMTUNCndpZHRoX3NhbWVfY2VsbF9zYW1lX3RhcmdldF90eXBlID0gMC41DQoNCmdhcHMgPSBjKA0KICAjIHdpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIlBhbkNLKyIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgd2lkdGhfZGlmZmVyZW50X2NlbGwsDQogICMgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiR2VuZXJhbCIpICU+JSBkaXN0aW5jdChsaWdhbmQpICU+JSBucm93KCkgLTEpKSwNCiAgIyB3aWR0aF9kaWZmZXJlbnRfY2VsbCwNCiAgI3JlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV9saWdhbmRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIobGlnYW5kX3R5cGUgPT0gIkVuZG90aGVsaWFsLXNwZWNpZmljIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLCANCiAgI3dpZHRoX2xpZ2FuZF90YXJnZXQsDQogIHJlcCh3aWR0aF9zYW1lX2NlbGxfc2FtZV90YXJnZXRfdHlwZSwgdGltZXMgPSAoY2lyY29zX2xpbmtzICU+JSBmaWx0ZXIodGFyZ2V0X3R5cGUgPT0gIkNEMTArIikgJT4lIGRpc3RpbmN0KHRhcmdldCkgJT4lIG5yb3coKSAtMSkpLA0KICB3aWR0aF9saWdhbmRfdGFyZ2V0DQogICkNCg0KI2NpcmNvcy5wYXIoZ2FwLmRlZ3JlZSA9IGdhcHMpDQoNCmNpcmNvcy5wYXIoUkVTRVQgPSBUUlVFKQ0KDQpjaG9yZERpYWdyYW0obGlua3NfY2lyY2xlLCANCiAgICAgICAgICAgICBkaXJlY3Rpb25hbCA9IDEsDQogICAgICAgICAgICAgI29yZGVyPW9yZGVyLA0KICAgICAgICAgICAgIGxpbmsuc29ydCA9IFRSVUUsIA0KICAgICAgICAgICAgIGxpbmsuZGVjcmVhc2luZyA9IEZBTFNFLCANCiAgICAgICAgICAgICBncmlkLmNvbCA9IGdyaWRfY29sLA0KICAgICAgICAgICAgIHRyYW5zcGFyZW5jeSA9IHRyYW5zcGFyZW5jeSwgDQogICAgICAgICAgICAgZGlmZkhlaWdodCA9IDAuMDA1LCANCiAgICAgICAgICAgICBkaXJlY3Rpb24udHlwZSA9IGMoImRpZmZIZWlnaHQiLCAiYXJyb3dzIiksDQogICAgICAgICAgICAgbGluay5hcnIudHlwZSA9ICJiaWcuYXJyb3ciLCANCiAgICAgICAgICAgICBsaW5rLnZpc2libGUgPSBsaW5rc19jaXJjbGUkd2VpZ2h0ID49IGN1dG9mZl9pbmNsdWRlX2FsbF9saWdhbmRzLA0KICAgICAgICAgICAgIGFubm90YXRpb25UcmFjayA9ICJncmlkIiwNCiAgICAgICAgICAgICBwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gMC4wNzUpKQ0KICAgICAgICAgICAgICNwcmVBbGxvY2F0ZVRyYWNrcyA9IGxpc3QodHJhY2suaGVpZ2h0ID0gbWF4KHN0cndpZHRoKHVubGlzdChkaW1uYW1lcyhsaW5rc19jaXJjbGUpKSkpKSkgIA0KICAgICAgICAgICAgIA0KIyB3ZSBnbyBiYWNrIHRvIHRoZSBmaXJzdCB0cmFjayBhbmQgY3VzdG9taXplIHNlY3RvciBsYWJlbHMNCmNpcmNvcy50cmFjayh0cmFjay5pbmRleCA9IDEsIHBhbmVsLmZ1biA9IGZ1bmN0aW9uKHgsIHkpIHsNCiAgICBjaXJjb3MudGV4dChDRUxMX01FVEEkeGNlbnRlciwgQ0VMTF9NRVRBJHlsaW1bMV0sIENFTExfTUVUQSRzZWN0b3IuaW5kZXgsDQogICAgICAgIGZhY2luZyA9ICJjbG9ja3dpc2UiLCBuaWNlRmFjaW5nID0gVFJVRSwgYWRqID0gYygwLCAwLjU1KSwgY2V4ID0gMSkNCn0sIGJnLmJvcmRlciA9IE5BKSANCg0KbGVnZW5kKCJib3R0b21sZWZ0IiwgDQogICAgICAgdGl0bGUgPSAiQ2VsbCBzcGVjaWZpYyIsDQogICAgICAgY2V4PSAzLCANCiAgICAgICBsZWdlbmQgPSB1bmlxdWUoYyhjaXJjb3NfbGlua3MkbGlnYW5kX3R5cGUsIHVuaXF1ZShjaXJjb3NfbGlua3MkdGFyZ2V0X3R5cGUpKSksDQogICAgICAgZmlsbCA9IHVuaXF1ZShjKGdyaWRfY29sKSkpDQp0aXRsZSgiTGlnYW5kLXJlY2VwdG9yIGNvbm5lY3Rpb25zIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTB9DQojIGdldCB0aGUgbGlnYW5kLXJlY2VwdG9yIG5ldHdvcmsgb2YgdGhlIHRvcC1yYW5rZWQgbGlnYW5kcw0KbHJfbmV0d29ya190b3AgPSBscl9uZXR3b3JrICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGJlc3RfdXBzdHJlYW1fbGlnYW5kcyAmIHRvICVpbiUgZXhwcmVzc2VkX3JlY2VwdG9ycykgJT4lIGRpc3RpbmN0KGZyb20sdG8pDQpiZXN0X3Vwc3RyZWFtX3JlY2VwdG9ycyA9IGxyX25ldHdvcmtfdG9wICU+JSBwdWxsKHRvKSAlPiUgdW5pcXVlKCkNCg0KIyAjIGdldCB0aGUgd2VpZ2h0cyBvZiB0aGUgbGlnYW5kLXJlY2VwdG9yIGludGVyYWN0aW9ucyBhcyB1c2VkIGluIHRoZSBOaWNoZU5ldCBtb2RlbA0KIyB3ZWlnaHRlZF9uZXR3b3JrcyA9IHJlYWRSRFModXJsKCJodHRwczovL3plbm9kby5vcmcvcmVjb3JkLzMyNjA3NTgvZmlsZXMvd2VpZ2h0ZWRfbmV0d29ya3MucmRzIikpDQpscl9uZXR3b3JrX3RvcF9kZiA9IHdlaWdodGVkX25ldHdvcmtzJGxyX3NpZyAlPiUgZmlsdGVyKGZyb20gJWluJSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgJiB0byAlaW4lIGJlc3RfdXBzdHJlYW1fcmVjZXB0b3JzKQ0KDQojIGNvbnZlcnQgdG8gYSBtYXRyaXgNCmxyX25ldHdvcmtfdG9wX2RmID0gbHJfbmV0d29ya190b3BfZGYgJT4lIHNwcmVhZCgiZnJvbSIsIndlaWdodCIsZmlsbCA9IDApDQpscl9uZXR3b3JrX3RvcF9tYXRyaXggPSBscl9uZXR3b3JrX3RvcF9kZiAlPiUgc2VsZWN0KC10bykgJT4lIGFzLm1hdHJpeCgpICU+JSBtYWdyaXR0cjo6c2V0X3Jvd25hbWVzKGxyX25ldHdvcmtfdG9wX2RmJHRvKQ0KDQojIHBlcmZvcm0gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdG8gb3JkZXIgdGhlIGxpZ2FuZHMgYW5kIHJlY2VwdG9ycw0KZGlzdF9yZWNlcHRvcnMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCwgbWV0aG9kID0gImJpbmFyeSIpDQpoY2x1c3RfcmVjZXB0b3JzID0gaGNsdXN0KGRpc3RfcmVjZXB0b3JzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpvcmRlcl9yZWNlcHRvcnMgPSBoY2x1c3RfcmVjZXB0b3JzJGxhYmVsc1toY2x1c3RfcmVjZXB0b3JzJG9yZGVyXQ0KDQpkaXN0X2xpZ2FuZHMgPSBkaXN0KGxyX25ldHdvcmtfdG9wX21hdHJpeCAlPiUgdCgpLCBtZXRob2QgPSAiYmluYXJ5IikNCmhjbHVzdF9saWdhbmRzID0gaGNsdXN0KGRpc3RfbGlnYW5kcywgbWV0aG9kID0gIndhcmQuRDIiKQ0Kb3JkZXJfbGlnYW5kc19yZWNlcHRvciA9IGhjbHVzdF9saWdhbmRzJGxhYmVsc1toY2x1c3RfbGlnYW5kcyRvcmRlcl0NCg0KDQp2aXNfbGlnYW5kX3JlY2VwdG9yX25ldHdvcmsgPSBscl9uZXR3b3JrX3RvcF9tYXRyaXhbb3JkZXJfcmVjZXB0b3JzLCBvcmRlcl9saWdhbmRzX3JlY2VwdG9yXQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yayA9IHZpc19saWdhbmRfcmVjZXB0b3JfbmV0d29yayAlPiUgdCgpICU+JSBtYWtlX2hlYXRtYXBfZ2dwbG90KCJQcmlvcml0aXplZCBQYW5DSytsaWdhbmRzIiwiUmVjZXB0b3JzIGV4cHJlc3NlZCBieSBDRDEwKyIsIGNvbG9yID0gIm1lZGl1bXZpb2xldHJlZCIsIHhfYXhpc19wb3NpdGlvbiA9ICJ0b3AiLGxlZ2VuZF90aXRsZSA9ICJQcmlvciBpbnRlcmFjdGlvbiBwb3RlbnRpYWwiKQ0KcF9saWdhbmRfcmVjZXB0b3JfbmV0d29yaw0KYGBgDQoNCiMgQ05WDQoNCmBgYHtyfQ0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJpbmZlcmNudiIpDQpsaWJyYXJ5KGluZmVyY252KQ0KI2RldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiYWVyaWNrc28vU3BhdGlhbEluZmVyQ05WIikNCiNsaWJyYXJ5KFNwYXRpYWxJbmZlckNOVikNCmBgYA0KDQpgYGB7ciBjcmVhdGUgY252IGZpbGVzX30NCmZpbGUgPC0gcmVhZExpbmVzKFBLQ0ZpbGVzKQ0KZmlsZSA8LSBnc3ViKCcgJywgJycsDQogICAgICAgIGdzdWIoJyInLCAnJywgZmlsZSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBhbmQgcmVtb3ZlIHNwYWNlDQoNCmRpc3BsYXkgPC0gZmlsZVtncmVwbCgiRGlzcGxheU5hbWUiLCBmaWxlLCBmaXhlZCA9IFRSVUUpXSAgICAgICAgICMgR3JhYiBkaXNwbGF5IG5hbWVzDQpkaXNwbGF5IDwtIGdzdWIoJ0Rpc3BsYXlOYW1lJywgIiIsDQogICAgICAgICAgIGdzdWIoJyInLCAiIiwNCiAgICAgICAgICAgZ3N1YignOicsICIiLA0KICAgICAgICAgICBnc3ViKCcsJywgIiIsDQogICAgICAgICAgIGdzdWIoJyAnLCAiIiwNCiAgICAgICAgICAgZ3N1YignXzAxJywgIiIsIGRpc3BsYXkpKSkpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhYiBvbmx5IHRoZSBuYW1lcw0KZGlzcGxheSA8LSB1bmlxdWUoZGlzcGxheSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZHVwbGljYXRlcw0KDQpjaHIgPC0gZmlsZVtncmVwKCJHZW5vbWVDb29yZGluYXRlcyIsIGZpbGUpKzFdICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY2hyIHBvc2l0aW9ucyB1bmRlciB0aGUgR2Vub21lQ29vcmRpbmF0ZXMgbGluZQ0KY2hyIDwtZ3N1YignLCcsICIiLCBjaHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW53YW50ZWQgc3ltYm9scw0KDQpwb3NpdGlvbnMgPC0gZGF0YS5mcmFtZSgNCiAgbmFtZSA9IGRpc3BsYXksIA0KICBjaHIgPSBjaHIpDQpwb3NpdGlvbnMgPC0gcG9zaXRpb25zWyFncmVwbCgiVGFyZ2V0U2VxdWVuY2UiLCBwb3NpdGlvbnMkY2hyKSxdICAjIFJlbW92ZSBlbnRyaWVzIHdpdGhvdXQgY29vcmRpbmF0ZXMNCg0KcG9zaXRpb25zJGNociA8LSBnc3ViKCc6JywgJy0nLCBwb3NpdGlvbnMkY2hyKSAgICAgICAgICAgICAgICAgICAgIyBGb3JtYXQgZm9yIHNwbGl0dGluZw0KcG9zaXRpb25zJGNociA8LSBzdHJfc3BsaXRfZml4ZWQocG9zaXRpb25zJGNociwgIi0iLCAzKSAgICAgICAgICAgIyBTcGxpdCBpbnRvIGNociwgYmVnaW4sIGFuZCBlbmQgcG9zaXRpb24NCnBvc2l0aW9ucyA8LSBhcy5tYXRyaXgocG9zaXRpb25zKSANCnBvc2l0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHBvc2l0aW9ucykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU3dhcCB0eXBlcyB0byByZWNvZ25pemUgc3BsaXQgY29sdW1ucyBhcyBzaW5ndWxhciBjb2x1bW5zDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIE5VTEwNCnJvd25hbWVzKHBvc2l0aW9ucykgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCg0Kd3JpdGUudGFibGUocG9zaXRpb25zLCAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsIA0KICAgICAgICAgICAgc2VwID0gIlx0IiwNCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsIA0KICAgICAgICAgICAgY29sLm5hbWVzID0gRkFMU0UsIA0KICAgICAgICAgICAgcm93Lm5hbWVzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXcml0ZSBhd2F5DQoNCmNvdW50cyA8LSB0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXQ0Kd3JpdGUudGFibGUoY291bnRzLCAiQ291bnRzLnRzdiIsIHNlcCA9ICJcdCIpICAgICAgICAgICAgICAgICAgICAjIE1ha2UgcmF3IGNvdW50IGZpbGUNCg0KYW5ub3RhdGlvbiA8LSB0YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YQ0KYW5ub3RhdGlvbiRTYW1wbGVJRCA8LSByb3duYW1lcyhhbm5vdGF0aW9uKQ0KYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgIkFOTjIiKV0gICAgICAgICAgICAgICAgICAjIFNlbGVjdCBTYW1wbGVJRCBhbmQgdGhlIG5lZWRlZCBhbm5vdGF0aW9uIChDTlYgcmVxdWlyZXMgU2FtcGxlSUQgYW5kIG9ubHkgMSBhbm5vdGF0aW9uKQ0Kcm93bmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTA0KY29sbmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQp3cml0ZS50YWJsZShhbm5vdGF0aW9uLCAiQW5ub3RhdGlvbnMudHN2IiwgDQogICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSwgDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhbm5vdGF0aW9uIGZpbGUNCmBgYA0KDQoNCmBgYHtyIHJ1biBDTlZfLCBpbmNsdWRlPUZBTFNFfQ0KIyBQaWNrIG5vcm1hbC9yZWZlcmVuY2UgZ3JvdXAocykNCnJlZmVyZW5jZSA8LSBjKCJDb3J0aWNhbCBnbG9tZXJ1bHVzIikNCg0KIyBOYW1lIG91dHB1dCBmb2xkZXINCm91dF9kaXIgPC0gIkNOViINCg0KIyBDcmVhdGUgdGhlIGluZmVyY252IG9iamVjdA0KaW5mZXJjbnZfb2JqID0gQ3JlYXRlSW5mZXJjbnZPYmplY3QocmF3X2NvdW50c19tYXRyaXg9ICJDb3VudHMudHN2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9ICJBbm5vdGF0aW9ucy50c3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09Ilx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfb3JkZXJfZmlsZT0gImdlbmVfb3JkZXJfSHNfV1RBX3YxX3BrYy50eHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2dlbmVfb3JkZXJfZmlsZT0gImdlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWZfZ3JvdXBfbmFtZXM9IHJlZmVyZW5jZSwgIyBpbnB1dCB0aGUgbm9ybWFsL3JlZmVyZW5jZSBncm91cCBuYW1lcw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hyX2V4Y2x1ZGUgPSBjKCJjaHJNIikpICMgRGVmYXVsdCBleGNsdWRlcyBjaHJYLCBjaHJZIGFuZCBjaHJNLiBCeSBvbmx5IHBpY2tpbmcgY2hyTSB5b3UgaW5jbHVkZSB0aGUgWCBhbmQgWSBjaHJvbW9zb21lcy4gICAgIA0KDQojIHBlcmZvcm0gaW5mZXJjbnYgb3BlcmF0aW9ucyB0byByZXZlYWwgY252IHNpZ25hbC4gRm9yIGFsbCBvcHRpb25zOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2Jyb2FkaW5zdGl0dXRlL2luZmVyY252L21hbi9ydW4uaHRtbA0KaW5mZXJjbnZfb2JqID0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1dG9mZj0wLjEsICAjIHVzZSAxIGZvciBzbWFydC1zZXEsIDAuMSBmb3IgMTB4LWdlbm9taWNzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dF9kaXI9IG91dF9kaXIsICAjIGRpciBpcyBhdXRvLWNyZWF0ZWQgZm9yIHN0b3Jpbmcgb3V0cHV0cw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1GLCAgICMgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhNTT1GLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmFseXNpc19tb2RlID0gInN1YmNsdXN0ZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyxkZWJ1Zz1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KICAgICAgICAgICMgIkdlbm9tZUNvb3JkaW5hdGVzIjogWw0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc4NzMxLTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzg3Mzk2LTExMjM5MzA2OSIsIA0KICAgICAgICAgICMgICAiY2hyMTM6MTEyMzc2NDc5LTExMjM5MzA2OSINCmBgYA0KDQpgYGB7ciBzaG93IENOVl8sIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0yNX0NCmltZyA8LSByZWFkUE5HKHBhc3RlKG91dF9kaXIsICIvaW5mZXJjbnYucG5nIiwgc2VwID0gIiIpKQ0KZ3JpZDo6Z3JpZC5uZXdwYWdlKCkNCmdyaWQ6OmdyaWQucmFzdGVyKGltZykNCmBgYA0KDQo=